Merge branch 'new-ui--main-screens--v2' into 'master'

Improved UI for new main screens

Closes #734, #867, and #882

See merge request !434
This commit is contained in:
Peter Serwylo 2017-03-13 23:55:23 +00:00
commit c6f7eefddd
101 changed files with 2667 additions and 518 deletions

View File

@ -69,6 +69,7 @@ connected24:
cat "$log" | curl --silent -F 'clbin=<-' https://clbin.com; cat "$log" | curl --silent -F 'clbin=<-' https://clbin.com;
done done
- exit $EXITVALUE - exit $EXITVALUE
allow_failure: true
after_script: after_script:
# this file changes every time but should not be cached # this file changes every time but should not be cached

View File

@ -50,7 +50,7 @@ dependencies {
testCompile 'junit:junit:4.12' testCompile 'junit:junit:4.12'
testCompile "org.robolectric:robolectric:3.1.2" testCompile "org.robolectric:robolectric:3.3.1"
// As per https://github.com/robolectric/robolectric/issues/1932#issuecomment-219796474 // As per https://github.com/robolectric/robolectric/issues/1932#issuecomment-219796474
testCompile 'org.khronos:opengl-api:gl1.1-android-2.1_r1' testCompile 'org.khronos:opengl-api:gl1.1-android-2.1_r1'

View File

@ -135,8 +135,179 @@
android:launchMode="singleTop" android:launchMode="singleTop"
android:windowSoftInputMode="adjustResize" android:windowSoftInputMode="adjustResize"
android:configChanges="layoutDirection|locale|keyboardHidden|orientation|screenSize" > android:configChanges="layoutDirection|locale|keyboardHidden|orientation|screenSize" >
</activity>
<activity
android:name=".privileged.views.InstallConfirmActivity"
android:label="@string/menu_install"
android:theme="@style/MinWithDialogBaseThemeLight"
android:excludeFromRecents="true"
android:parentActivityName=".views.main.MainActivity"
android:configChanges="layoutDirection|locale" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".views.main.MainActivity" />
</activity>
<activity
android:name=".privileged.views.UninstallDialogActivity"
android:excludeFromRecents="true"
android:theme="@style/AppThemeTransparent" />
<activity
android:name=".views.ManageReposActivity"
android:label="@string/app_name"
android:launchMode="singleTask"
android:parentActivityName=".views.main.MainActivity"
android:configChanges="layoutDirection|locale" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".views.main.MainActivity" />
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/vnd.org.fdroid.fdroid.repo" />
</intent-filter>
</activity>
<activity
android:name=".NfcNotEnabledActivity"
android:noHistory="true"
android:configChanges="layoutDirection|locale" />
<activity
android:name=".views.RepoDetailsActivity"
android:label="@string/repo_details"
android:parentActivityName=".views.ManageReposActivity"
android:windowSoftInputMode="stateHidden"
android:configChanges="layoutDirection|locale" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".views.ManageReposActivity" />
</activity>
<activity
android:name=".AppDetails"
android:label="@string/app_details"
android:exported="true"
android:parentActivityName=".FDroid"
android:configChanges="layoutDirection|locale" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".FDroid" />
</activity>
<activity
android:name=".AppDetails2"
android:label="@string/app_details"
android:exported="true"
android:parentActivityName=".views.main.MainActivity"
android:configChanges="layoutDirection|locale" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".views.main.MainActivity" />
</activity>
<activity
android:label="@string/menu_settings"
android:name=".PreferencesActivity"
android:parentActivityName=".FDroid" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".FDroid" />
</activity>
<activity
android:name=".acra.CrashReportActivity"
android:theme="@style/AppThemeDark"
android:process=":error_report"
android:launchMode="singleInstance"
android:excludeFromRecents="true"
android:finishOnTaskLaunch="true" />
<activity
android:label="@string/swap"
android:name=".views.swap.SwapWorkflowActivity"
android:parentActivityName=".views.main.MainActivity"
android:theme="@style/SwapTheme.Wizard"
android:screenOrientation="portrait"
android:configChanges="orientation|keyboardHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".views.main.MainActivity" />
</activity>
<!-- Note: AppThemeTransparent, this activity shows dialogs only -->
<activity
android:name=".privileged.install.InstallExtensionDialogActivity"
android:theme="@style/AppThemeTransparent" />
<activity android:name=".data.ObbUrlActivity"
android:theme="@android:style/Theme.NoDisplay" />
<receiver
android:name=".privileged.install.InstallExtensionBootReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<!-- Note: AppThemeTransparent, this activity shows dialogs only -->
<activity
android:name=".installer.DefaultInstallerActivity"
android:theme="@style/AppThemeTransparent" />
<!-- Note: AppThemeTransparent, this activity shows dialogs only -->
<activity
android:name=".installer.ErrorDialogActivity"
android:theme="@style/AppThemeTransparent" />
<receiver android:name=".receiver.StartupReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.HOME" />
</intent-filter>
</receiver>
<receiver android:name=".receiver.PackageManagerReceiver">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED" />
<action android:name="android.intent.action.PACKAGE_CHANGED" />
<action android:name="android.intent.action.PACKAGE_REMOVED" />
<data android:scheme="package" />
</intent-filter>
</receiver>
<receiver android:name=".receiver.WifiStateChangeReceiver" >
<intent-filter>
<action android:name="android.net.wifi.STATE_CHANGE" />
</intent-filter>
</receiver>
<service android:name=".UpdateService" />
<service
android:name=".net.DownloaderService"
android:exported="false" />
<service
android:name=".installer.InstallerService"
android:exported="false" />
<service
android:name=".CleanCacheService"
android:exported="false" />
<service android:name=".net.WifiStateChangeService" />
<service android:name=".localrepo.SwapService" />
<service
android:name=".installer.InstallManagerService"
android:exported="false" />
<service
android:name=".installer.InstallHistoryService"
android:exported="false" />
<service
android:name=".localrepo.CacheSwapAppsService"
android:exported="false" />
<service
android:name=".data.InstalledAppProviderService"
android:exported="false" />
<activity android:name=".views.main.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<!-- App URLs --> <!-- App URLs -->
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.VIEW" />
@ -215,6 +386,7 @@
<data android:path="/gp/mas/dl/android" /> <data android:path="/gp/mas/dl/android" />
</intent-filter> </intent-filter>
<!-- Search URLs --> <!-- Search URLs -->
<intent-filter> <intent-filter>
@ -255,19 +427,6 @@
android:name="android.app.searchable" android:name="android.app.searchable"
android:resource="@xml/searchable" /> android:resource="@xml/searchable" />
<!-- Handle NFC tags detected from outside our application -->
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<!--
URIs that come in via NFC have scheme/host normalized to all lower case
https://developer.android.com/reference/android/nfc/NfcAdapter.html#ACTION_NDEF_DISCOVERED
-->
<data android:scheme="fdroidrepo" />
<data android:scheme="fdroidrepos" />
</intent-filter>
<!-- Repo URLs --> <!-- Repo URLs -->
@ -280,7 +439,7 @@
The reason for this is that the only differentiating factor is the presence The reason for this is that the only differentiating factor is the presence
of a "swap=1" in the query string, and intent-filter is unable to deal with of a "swap=1" in the query string, and intent-filter is unable to deal with
query parameters. An alternative would be to do something like fdroidswap:// as query parameters. An alternative would be to do something like fdroidswap:// as
a scheme, but then we. Need to copy/paste all of this intent-filter stuff and a scheme, but then we need to copy/paste all of this intent-filter stuff and
keep it up to date when it changes or a bug is found. keep it up to date when it changes or a bug is found.
--> -->
<intent-filter> <intent-filter>
@ -337,179 +496,34 @@
<data android:pathPattern="/.*/.*/FDROID/REPO" /> <data android:pathPattern="/.*/.*/FDROID/REPO" />
<data android:pathPattern="/.*/.*/.*/FDROID/REPO" /> <data android:pathPattern="/.*/.*/.*/FDROID/REPO" />
</intent-filter> </intent-filter>
</activity>
<activity
android:name=".privileged.views.InstallConfirmActivity" <!-- Handle NFC tags detected from outside our application -->
android:label="@string/menu_install"
android:theme="@style/MinWithDialogBaseThemeLight"
android:excludeFromRecents="true"
android:parentActivityName=".FDroid"
android:configChanges="layoutDirection|locale" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".FDroid" />
</activity>
<activity
android:name=".privileged.views.UninstallDialogActivity"
android:excludeFromRecents="true"
android:theme="@style/AppThemeTransparent" />
<activity
android:name=".views.ManageReposActivity"
android:label="@string/app_name"
android:launchMode="singleTask"
android:parentActivityName=".FDroid"
android:configChanges="layoutDirection|locale" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".FDroid" />
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW" /> <action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/vnd.org.fdroid.fdroid.repo" /> <!--
URIs that come in via NFC have scheme/host normalized to all lower case
https://developer.android.com/reference/android/nfc/NfcAdapter.html#ACTION_NDEF_DISCOVERED
-->
<data android:scheme="fdroidrepo" />
<data android:scheme="fdroidrepos" />
</intent-filter> </intent-filter>
</activity>
<activity
android:name=".NfcNotEnabledActivity"
android:noHistory="true"
android:configChanges="layoutDirection|locale" />
<activity
android:name=".views.RepoDetailsActivity"
android:label="@string/repo_details"
android:parentActivityName=".views.ManageReposActivity"
android:windowSoftInputMode="stateHidden"
android:configChanges="layoutDirection|locale" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".views.ManageReposActivity" />
</activity>
<activity
android:name=".AppDetails"
android:label="@string/app_details"
android:exported="true"
android:parentActivityName=".FDroid"
android:configChanges="layoutDirection|locale" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".FDroid" />
</activity>
<activity
android:name=".AppDetails2"
android:label="@string/app_details"
android:exported="true"
android:parentActivityName=".FDroid"
android:configChanges="layoutDirection|locale" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".FDroid" />
</activity>
<activity
android:label="@string/menu_settings"
android:name=".PreferencesActivity"
android:parentActivityName=".FDroid" >
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".FDroid" />
</activity>
<activity
android:name=".acra.CrashReportActivity"
android:theme="@style/AppThemeDark"
android:process=":error_report"
android:launchMode="singleInstance"
android:excludeFromRecents="true"
android:finishOnTaskLaunch="true" />
<activity
android:label="@string/swap"
android:name=".views.swap.SwapWorkflowActivity"
android:parentActivityName=".FDroid"
android:theme="@style/SwapTheme.Wizard"
android:screenOrientation="portrait"
android:configChanges="orientation|keyboardHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".FDroid" />
</activity>
<!-- Note: AppThemeTransparent, this activity shows dialogs only -->
<activity
android:name=".privileged.install.InstallExtensionDialogActivity"
android:theme="@style/AppThemeTransparent" />
<activity android:name=".data.ObbUrlActivity"
android:theme="@android:style/Theme.NoDisplay" />
<receiver
android:name=".privileged.install.InstallExtensionBootReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<!-- Note: AppThemeTransparent, this activity shows dialogs only -->
<activity
android:name=".installer.DefaultInstallerActivity"
android:theme="@style/AppThemeTransparent" />
<!-- Note: AppThemeTransparent, this activity shows dialogs only -->
<activity
android:name=".installer.ErrorDialogActivity"
android:theme="@style/AppThemeTransparent" />
<receiver android:name=".receiver.StartupReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.HOME" />
</intent-filter>
</receiver>
<receiver android:name=".receiver.PackageManagerReceiver">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED" />
<action android:name="android.intent.action.PACKAGE_CHANGED" />
<action android:name="android.intent.action.PACKAGE_REMOVED" />
<data android:scheme="package" />
</intent-filter>
</receiver>
<receiver android:name=".receiver.WifiStateChangeReceiver" >
<intent-filter>
<action android:name="android.net.wifi.STATE_CHANGE" />
</intent-filter>
</receiver>
<service android:name=".UpdateService" />
<service
android:name=".net.DownloaderService"
android:exported="false" />
<service
android:name=".installer.InstallerService"
android:exported="false" />
<service
android:name=".CleanCacheService"
android:exported="false" />
<service android:name=".net.WifiStateChangeService" />
<service android:name=".localrepo.SwapService" />
<service
android:name=".installer.InstallManagerService"
android:exported="false" />
<service
android:name=".installer.InstallHistoryService"
android:exported="false" />
<service
android:name=".localrepo.CacheSwapAppsService"
android:exported="false" />
<service
android:name=".data.InstalledAppProviderService"
android:exported="false" />
<activity android:name=".views.main.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity> </activity>
<activity android:name=".views.apps.AppListActivity" /> <activity android:name=".views.apps.AppListActivity" />
<activity android:name=".views.installed.InstalledAppsActivity"
android:parentActivityName=".views.main.MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".views.main.MainActivity" />
</activity>
</application> </application>
</manifest> </manifest>

View File

@ -16,6 +16,7 @@ import android.support.design.widget.CoordinatorLayout;
import android.support.v4.content.LocalBroadcastManager; import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.support.v7.graphics.Palette;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar; import android.support.v7.widget.Toolbar;
@ -23,12 +24,13 @@ import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.widget.ImageView; import android.view.View;
import android.widget.Toast; import android.widget.Toast;
import com.nostra13.universalimageloader.core.DisplayImageOptions; import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.assist.ImageScaleType; import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
import org.fdroid.fdroid.data.Apk; import org.fdroid.fdroid.data.Apk;
import org.fdroid.fdroid.data.ApkProvider; import org.fdroid.fdroid.data.ApkProvider;
@ -44,6 +46,7 @@ import org.fdroid.fdroid.net.Downloader;
import org.fdroid.fdroid.net.DownloaderService; import org.fdroid.fdroid.net.DownloaderService;
import org.fdroid.fdroid.views.AppDetailsRecyclerViewAdapter; import org.fdroid.fdroid.views.AppDetailsRecyclerViewAdapter;
import org.fdroid.fdroid.views.ShareChooserDialog; import org.fdroid.fdroid.views.ShareChooserDialog;
import org.fdroid.fdroid.views.apps.FeatureImage;
public class AppDetails2 extends AppCompatActivity implements ShareChooserDialog.ShareChooserDialogListener, AppDetailsRecyclerViewAdapter.AppDetailsRecyclerViewAdapterCallbacks { public class AppDetails2 extends AppCompatActivity implements ShareChooserDialog.ShareChooserDialogListener, AppDetailsRecyclerViewAdapter.AppDetailsRecyclerViewAdapterCallbacks {
@ -95,15 +98,37 @@ public class AppDetails2 extends AppCompatActivity implements ShareChooserDialog
recyclerView.setAdapter(adapter); recyclerView.setAdapter(adapter);
// Load the feature graphic, if present // Load the feature graphic, if present
if (!TextUtils.isEmpty(app.iconUrlLarge)) { if (!TextUtils.isEmpty(app.iconUrl)) {
ImageView ivFeatureGraphic = (ImageView) findViewById(R.id.feature_graphic); final FeatureImage featureImage = (FeatureImage) findViewById(R.id.feature_graphic);
DisplayImageOptions displayImageOptions = new DisplayImageOptions.Builder() DisplayImageOptions displayImageOptions = Utils.getImageLoadingOptions().build();
.cacheInMemory(false) ImageLoader.getInstance().loadImage(app.iconUrl, displayImageOptions, new ImageLoadingListener() {
.cacheOnDisk(true) @Override
.imageScaleType(ImageScaleType.NONE) public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
.bitmapConfig(Bitmap.Config.RGB_565) if (featureImage != null) {
.build(); new Palette.Builder(loadedImage).generate(new Palette.PaletteAsyncListener() {
ImageLoader.getInstance().displayImage(app.iconUrlLarge, ivFeatureGraphic, displayImageOptions); @Override
public void onGenerated(Palette palette) {
featureImage.setPalette(palette);
}
});
}
}
@Override
public void onLoadingStarted(String imageUri, View view) {
}
@Override
public void onLoadingFailed(String imageUri, View view, FailReason failReason) {
}
@Override
public void onLoadingCancelled(String imageUri, View view) {
}
});
} }
} }

View File

@ -38,7 +38,7 @@ public class NfcHelper {
} }
@TargetApi(16) @TargetApi(16)
static void setAndroidBeam(Activity activity, String packageName) { public static void setAndroidBeam(Activity activity, String packageName) {
if (Build.VERSION.SDK_INT < 16) { if (Build.VERSION.SDK_INT < 16) {
return; return;
} }

View File

@ -31,6 +31,7 @@ import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
import com.nostra13.universalimageloader.utils.DiskCacheUtils; import com.nostra13.universalimageloader.utils.DiskCacheUtils;
import org.fdroid.fdroid.data.App; import org.fdroid.fdroid.data.App;
import org.fdroid.fdroid.views.main.MainActivity;
import java.util.ArrayList; import java.util.ArrayList;
@ -408,7 +409,7 @@ class NotificationHelper {
} }
// Intent to open main app list // Intent to open main app list
Intent intentObject = new Intent(context, FDroid.class); Intent intentObject = new Intent(context, MainActivity.class);
PendingIntent piAction = PendingIntent.getActivity(context, 0, intentObject, 0); PendingIntent piAction = PendingIntent.getActivity(context, 0, intentObject, 0);
NotificationCompat.Builder builder = NotificationCompat.Builder builder =
@ -483,7 +484,7 @@ class NotificationHelper {
} }
// Intent to open main app list // Intent to open main app list
Intent intentObject = new Intent(context, FDroid.class); Intent intentObject = new Intent(context, MainActivity.class);
PendingIntent piAction = PendingIntent.getActivity(context, 0, intentObject, 0); PendingIntent piAction = PendingIntent.getActivity(context, 0, intentObject, 0);
NotificationCompat.Builder builder = NotificationCompat.Builder builder =

View File

@ -68,6 +68,7 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh
public static final String PREF_PROXY_PORT = "proxyPort"; public static final String PREF_PROXY_PORT = "proxyPort";
public static final String PREF_SHOW_NFC_DURING_SWAP = "showNfcDuringSwap"; public static final String PREF_SHOW_NFC_DURING_SWAP = "showNfcDuringSwap";
public static final String PREF_POST_PRIVILEGED_INSTALL = "postPrivilegedInstall"; public static final String PREF_POST_PRIVILEGED_INSTALL = "postPrivilegedInstall";
public static final String PREF_TRIED_EMPTY_UPDATE = "triedEmptyUpdate";
private static final boolean DEFAULT_ROOTED = true; private static final boolean DEFAULT_ROOTED = true;
private static final boolean DEFAULT_HIDE_ANTI_FEATURE_APPS = false; private static final boolean DEFAULT_HIDE_ANTI_FEATURE_APPS = false;
@ -182,6 +183,20 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh
} }
} }
/**
* Used the first time F-Droid is installed to flag whether or not we have tried to request
* apps from the repo. This is used so that when there is no apps available, we can differentiate
* between whether the repos actually have no apps (in which case we don't need to continue
* asking), or whether there is no apps because we have never actually asked to update the repos.
*/
public boolean hasTriedEmptyUpdate() {
return preferences.getBoolean(PREF_TRIED_EMPTY_UPDATE, false);
}
public void setTriedEmptyUpdate(boolean value) {
preferences.edit().putBoolean(PREF_TRIED_EMPTY_UPDATE, value).apply();
}
public boolean getUnstableUpdates() { public boolean getUnstableUpdates() {
return preferences.getBoolean(PREF_UNSTABLE_UPDATES, DEFAULT_UNSTABLE_UPDATES); return preferences.getBoolean(PREF_UNSTABLE_UPDATES, DEFAULT_UNSTABLE_UPDATES);
} }

View File

@ -50,6 +50,7 @@ import org.fdroid.fdroid.data.Repo;
import org.fdroid.fdroid.data.RepoProvider; import org.fdroid.fdroid.data.RepoProvider;
import org.fdroid.fdroid.data.Schema; import org.fdroid.fdroid.data.Schema;
import org.fdroid.fdroid.installer.InstallManagerService; import org.fdroid.fdroid.installer.InstallManagerService;
import org.fdroid.fdroid.views.main.MainActivity;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
@ -154,7 +155,7 @@ public class UpdateService extends IntentService {
// http://stackoverflow.com/a/20032920 // http://stackoverflow.com/a/20032920
// //
if (Build.VERSION.SDK_INT <= 10) { if (Build.VERSION.SDK_INT <= 10) {
Intent pendingIntent = new Intent(this, FDroid.class); Intent pendingIntent = new Intent(this, MainActivity.class);
pendingIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); pendingIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
notificationBuilder.setContentIntent(PendingIntent.getActivity(this, 0, pendingIntent, PendingIntent.FLAG_UPDATE_CURRENT)); notificationBuilder.setContentIntent(PendingIntent.getActivity(this, 0, pendingIntent, PendingIntent.FLAG_UPDATE_CURRENT));
} }

View File

@ -569,6 +569,10 @@ public class App extends ValueObject implements Comparable<App>, Parcelable {
return TextUtils.isEmpty(flattrID) ? null : "https://flattr.com/thing/" + flattrID; return TextUtils.isEmpty(flattrID) ? null : "https://flattr.com/thing/" + flattrID;
} }
/**
* @see App#suggestedVersionName for why this uses a getter while other member variables are
* publicly accessible.
*/
public String getSuggestedVersionName() { public String getSuggestedVersionName() {
return suggestedVersionName; return suggestedVersionName;
} }

View File

@ -199,26 +199,16 @@ public class AppProvider extends FDroidProvider {
public AppQuerySelection add(AppQuerySelection query) { public AppQuerySelection add(AppQuerySelection query) {
QuerySelection both = super.add(query); QuerySelection both = super.add(query);
AppQuerySelection bothWithJoin = new AppQuerySelection(both.getSelection(), both.getArgs()); AppQuerySelection bothWithJoin = new AppQuerySelection(both.getSelection(), both.getArgs());
ensureJoinsCopied(query, bothWithJoin); if (this.naturalJoinToInstalled() || query.naturalJoinToInstalled()) {
bothWithJoin.requireNaturalInstalledTable();
}
if (this.leftJoinToPrefs() || query.leftJoinToPrefs()) {
bothWithJoin.requireLeftJoinPrefs();
}
return bothWithJoin; return bothWithJoin;
} }
public AppQuerySelection not(AppQuerySelection query) {
QuerySelection both = super.not(query);
AppQuerySelection bothWithJoin = new AppQuerySelection(both.getSelection(), both.getArgs());
ensureJoinsCopied(query, bothWithJoin);
return bothWithJoin;
}
private void ensureJoinsCopied(AppQuerySelection toAdd, AppQuerySelection newlyCreated) {
if (this.naturalJoinToInstalled() || toAdd.naturalJoinToInstalled()) {
newlyCreated.requireNaturalInstalledTable();
}
if (this.leftJoinToPrefs() || toAdd.leftJoinToPrefs()) {
newlyCreated.requireLeftJoinPrefs();
}
}
} }
protected class Query extends QueryBuilder { protected class Query extends QueryBuilder {
@ -574,8 +564,7 @@ public class AppProvider extends FDroidProvider {
final String ignoreAll = "COALESCE(prefs." + AppPrefsTable.Cols.IGNORE_ALL_UPDATES + ", 0) != 1"; final String ignoreAll = "COALESCE(prefs." + AppPrefsTable.Cols.IGNORE_ALL_UPDATES + ", 0) != 1";
final String ignore = " (" + ignoreCurrent + " AND " + ignoreAll + ") "; final String ignore = " (" + ignoreCurrent + " AND " + ignoreAll + ") ";
final String nullChecks = app + "." + Cols.SUGGESTED_VERSION_CODE + " IS NOT NULL AND installed." + InstalledAppTable.Cols.VERSION_CODE + " IS NOT NULL "; final String where = ignore + " AND " + app + "." + Cols.SUGGESTED_VERSION_CODE + " > installed." + InstalledAppTable.Cols.VERSION_CODE;
final String where = nullChecks + " AND " + ignore + " AND " + app + "." + Cols.SUGGESTED_VERSION_CODE + " > installed." + InstalledAppTable.Cols.VERSION_CODE;
return new AppQuerySelection(where).requireNaturalInstalledTable().requireLeftJoinPrefs(); return new AppQuerySelection(where).requireNaturalInstalledTable().requireLeftJoinPrefs();
} }
@ -587,7 +576,7 @@ public class AppProvider extends FDroidProvider {
} }
private AppQuerySelection queryInstalled() { private AppQuerySelection queryInstalled() {
return new AppQuerySelection().requireNaturalInstalledTable().not(queryCanUpdate()); return new AppQuerySelection().requireNaturalInstalledTable();
} }
private AppQuerySelection querySearch(String query) { private AppQuerySelection querySearch(String query) {
@ -801,7 +790,11 @@ public class AppProvider extends FDroidProvider {
break; break;
case RECENTLY_UPDATED: case RECENTLY_UPDATED:
sortOrder = getTableName() + "." + Cols.LAST_UPDATED + " DESC"; String table = getTableName();
String isNew = table + "." + Cols.LAST_UPDATED + " <= " + table + "." + Cols.ADDED + " DESC";
String lastUpdated = table + "." + Cols.LAST_UPDATED + " DESC";
sortOrder = lastUpdated + ", " + isNew;
selection = selection.add(queryRecentlyUpdated()); selection = selection.add(queryRecentlyUpdated());
includeSwap = false; includeSwap = false;
break; break;

View File

@ -28,6 +28,7 @@ import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteOpenHelper;
import android.preference.PreferenceManager;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
@ -880,7 +881,8 @@ class DBHelper extends SQLiteOpenHelper {
private void resetTransient(SQLiteDatabase db) { private void resetTransient(SQLiteDatabase db) {
Utils.debugLog(TAG, "Removing app + apk tables so they can be recreated. Next time F-Droid updates it should trigger an index update."); Utils.debugLog(TAG, "Removing app + apk tables so they can be recreated. Next time F-Droid updates it should trigger an index update.");
context.getSharedPreferences("FDroid", Context.MODE_PRIVATE)
PreferenceManager.getDefaultSharedPreferences(context)
.edit() .edit()
.putBoolean("triedEmptyUpdate", false) .putBoolean("triedEmptyUpdate", false)
.apply(); .apply();
@ -924,8 +926,12 @@ class DBHelper extends SQLiteOpenHelper {
if (oldVersion >= 42) { if (oldVersion >= 42) {
return; return;
} }
context.getSharedPreferences("FDroid", Context.MODE_PRIVATE).edit()
.putBoolean("triedEmptyUpdate", false).apply(); PreferenceManager.getDefaultSharedPreferences(context)
.edit()
.putBoolean("triedEmptyUpdate", false)
.apply();
db.execSQL("drop table " + AppMetadataTable.NAME); db.execSQL("drop table " + AppMetadataTable.NAME);
db.execSQL("drop table " + ApkTable.NAME); db.execSQL("drop table " + ApkTable.NAME);
clearRepoEtags(db); clearRepoEtags(db);

View File

@ -78,8 +78,4 @@ public class QuerySelection {
return new QuerySelection(s, a); return new QuerySelection(s, a);
} }
public QuerySelection not(QuerySelection querySelection) {
String where = " NOT (" + querySelection.getSelection() + ") ";
return add(where, querySelection.getArgs());
}
} }

View File

@ -62,7 +62,7 @@ public class WifiStateChangeService extends IntentService {
} }
Utils.debugLog(TAG, "WiFi change service started, clearing info about wifi state until we have figured it out again."); Utils.debugLog(TAG, "WiFi change service started, clearing info about wifi state until we have figured it out again.");
NetworkInfo ni = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO); NetworkInfo ni = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
wifiManager = (WifiManager) getSystemService(WIFI_SERVICE); wifiManager = (WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE);
int wifiState = wifiManager.getWifiState(); int wifiState = wifiManager.getWifiState();
if (ni == null || ni.isConnected()) { if (ni == null || ni.isConnected()) {
Utils.debugLog(TAG, "ni == " + ni + " wifiState == " + printWifiState(wifiState)); Utils.debugLog(TAG, "ni == " + ni + " wifiState == " + printWifiState(wifiState));

View File

@ -32,10 +32,10 @@ import android.text.Html;
import android.util.Log; import android.util.Log;
import android.view.ContextThemeWrapper; import android.view.ContextThemeWrapper;
import org.fdroid.fdroid.FDroid;
import org.fdroid.fdroid.FDroidApp; import org.fdroid.fdroid.FDroidApp;
import org.fdroid.fdroid.R; import org.fdroid.fdroid.R;
import org.fdroid.fdroid.installer.PrivilegedInstaller; import org.fdroid.fdroid.installer.PrivilegedInstaller;
import org.fdroid.fdroid.views.main.MainActivity;
import java.io.File; import java.io.File;
@ -259,7 +259,7 @@ public class InstallExtensionDialogActivity extends FragmentActivity {
public void onClick(DialogInterface dialogInterface, int i) { public void onClick(DialogInterface dialogInterface, int i) {
InstallExtensionDialogActivity.this.setResult(result); InstallExtensionDialogActivity.this.setResult(result);
InstallExtensionDialogActivity.this.finish(); InstallExtensionDialogActivity.this.finish();
startActivity(new Intent(InstallExtensionDialogActivity.this, FDroid.class)); startActivity(new Intent(InstallExtensionDialogActivity.this, MainActivity.class));
} }
}) })
.setCancelable(false); .setCancelable(false);

View File

@ -32,7 +32,6 @@ import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.v4.app.LoaderManager; import android.support.v4.app.LoaderManager;
import android.support.v4.app.NavUtils;
import android.support.v4.content.CursorLoader; import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader; import android.support.v4.content.Loader;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
@ -52,7 +51,6 @@ import android.widget.ListView;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import org.fdroid.fdroid.FDroid;
import org.fdroid.fdroid.FDroidApp; import org.fdroid.fdroid.FDroidApp;
import org.fdroid.fdroid.R; import org.fdroid.fdroid.R;
import org.fdroid.fdroid.UpdateService; import org.fdroid.fdroid.UpdateService;
@ -149,11 +147,6 @@ public class ManageReposActivity extends AppCompatActivity implements LoaderMana
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) { switch (item.getItemId()) {
case android.R.id.home:
Intent destIntent = new Intent(this, FDroid.class);
setResult(RESULT_OK, destIntent);
NavUtils.navigateUpTo(this, destIntent);
return true;
case R.id.action_add_repo: case R.id.action_add_repo:
showAddRepo(); showAddRepo();
return true; return true;
@ -679,7 +672,7 @@ public class ManageReposActivity extends AppCompatActivity implements LoaderMana
private void checkIfNewRepoOnSameWifi(NewRepoConfig newRepo) { private void checkIfNewRepoOnSameWifi(NewRepoConfig newRepo) {
// if this is a local repo, check we're on the same wifi // if this is a local repo, check we're on the same wifi
if (!TextUtils.isEmpty(newRepo.getBssid())) { if (!TextUtils.isEmpty(newRepo.getBssid())) {
WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE); WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
WifiInfo wifiInfo = wifiManager.getConnectionInfo(); WifiInfo wifiInfo = wifiManager.getConnectionInfo();
String bssid = wifiInfo.getBSSID(); String bssid = wifiInfo.getBSSID();
if (TextUtils.isEmpty(bssid)) { /* not all devices have wifi */ if (TextUtils.isEmpty(bssid)) { /* not all devices have wifi */

View File

@ -11,8 +11,12 @@ import android.support.v4.content.Loader;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.view.KeyEvent;
import android.view.View; import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText; import android.widget.EditText;
import android.widget.TextView;
import org.fdroid.fdroid.R; import org.fdroid.fdroid.R;
import org.fdroid.fdroid.data.AppProvider; import org.fdroid.fdroid.data.AppProvider;
@ -21,6 +25,7 @@ import org.fdroid.fdroid.data.Schema;
public class AppListActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor>, CategoryTextWatcher.SearchTermsChangedListener { public class AppListActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor>, CategoryTextWatcher.SearchTermsChangedListener {
public static final String EXTRA_CATEGORY = "org.fdroid.fdroid.views.apps.AppListActivity.EXTRA_CATEGORY"; public static final String EXTRA_CATEGORY = "org.fdroid.fdroid.views.apps.AppListActivity.EXTRA_CATEGORY";
public static final String EXTRA_SEARCH_TERMS = "org.fdroid.fdroid.views.apps.AppListActivity.EXTRA_SEARCH_TERMS";
private RecyclerView appView; private RecyclerView appView;
private AppListAdapter appAdapter; private AppListAdapter appAdapter;
private String category; private String category;
@ -35,6 +40,21 @@ public class AppListActivity extends AppCompatActivity implements LoaderManager.
searchInput = (EditText) findViewById(R.id.search); searchInput = (EditText) findViewById(R.id.search);
searchInput.addTextChangedListener(new CategoryTextWatcher(this, searchInput, this)); searchInput.addTextChangedListener(new CategoryTextWatcher(this, searchInput, this));
searchInput.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
// Hide the keyboard (http://stackoverflow.com/a/1109108 (when pressing search)
InputMethodManager inputManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
inputManager.hideSoftInputFromWindow(searchInput.getWindowToken(), 0);
// Change focus from the search input to the app list.
appView.requestFocus();
return true;
}
return false;
}
});
View backButton = findViewById(R.id.back); View backButton = findViewById(R.id.back);
backButton.setOnClickListener(new View.OnClickListener() { backButton.setOnClickListener(new View.OnClickListener() {
@ -66,8 +86,9 @@ public class AppListActivity extends AppCompatActivity implements LoaderManager.
Intent intent = getIntent(); Intent intent = getIntent();
category = intent.hasExtra(EXTRA_CATEGORY) ? intent.getStringExtra(EXTRA_CATEGORY) : null; category = intent.hasExtra(EXTRA_CATEGORY) ? intent.getStringExtra(EXTRA_CATEGORY) : null;
searchTerms = intent.hasExtra(EXTRA_SEARCH_TERMS) ? intent.getStringExtra(EXTRA_SEARCH_TERMS) : null;
searchInput.setText(getSearchText(category, null)); searchInput.setText(getSearchText(category, searchTerms));
searchInput.setSelection(searchInput.getText().length()); searchInput.setSelection(searchInput.getText().length());
if (category != null) { if (category != null) {

View File

@ -1,15 +1,25 @@
package org.fdroid.fdroid.views.apps; package org.fdroid.fdroid.views.apps;
import android.annotation.TargetApi;
import android.app.Activity; import android.app.Activity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.Outline;
import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityOptionsCompat; import android.support.v4.app.ActivityOptionsCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v4.util.Pair; import android.support.v4.util.Pair;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.View; import android.view.View;
import android.widget.Button; import android.view.ViewOutlineProvider;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
@ -18,34 +28,85 @@ import com.nostra13.universalimageloader.core.ImageLoader;
import org.fdroid.fdroid.AppDetails; import org.fdroid.fdroid.AppDetails;
import org.fdroid.fdroid.AppDetails2; import org.fdroid.fdroid.AppDetails2;
import org.fdroid.fdroid.AppUpdateStatusManager;
import org.fdroid.fdroid.R; import org.fdroid.fdroid.R;
import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.Utils;
import org.fdroid.fdroid.data.Apk;
import org.fdroid.fdroid.data.ApkProvider; import org.fdroid.fdroid.data.ApkProvider;
import org.fdroid.fdroid.data.App; import org.fdroid.fdroid.data.App;
import org.fdroid.fdroid.data.AppPrefs;
import org.fdroid.fdroid.installer.ApkCache;
import org.fdroid.fdroid.installer.InstallManagerService; import org.fdroid.fdroid.installer.InstallManagerService;
import org.fdroid.fdroid.installer.Installer;
import org.fdroid.fdroid.installer.InstallerFactory;
import org.fdroid.fdroid.net.Downloader;
import org.fdroid.fdroid.net.DownloaderService;
import java.io.File;
// TODO: Support cancelling of downloads by tapping the install button a second time.
// TODO: Support installing of an app once downloaded by tapping the install button a second time.
public class AppListItemController extends RecyclerView.ViewHolder { public class AppListItemController extends RecyclerView.ViewHolder {
private static final String TAG = "AppListItemController";
private final Activity activity; private final Activity activity;
private final Button installButton; @NonNull
private final ImageView icon; private final ImageView icon;
@NonNull
private final TextView name; private final TextView name;
@Nullable
private final ImageView installButton;
@Nullable
private final TextView status; private final TextView status;
@Nullable
private final TextView installedVersion;
@Nullable
private final TextView ignoredStatus;
private final DisplayImageOptions displayImageOptions; private final DisplayImageOptions displayImageOptions;
private App currentApp; private App currentApp;
private String currentAppDownloadUrl;
public AppListItemController(Activity activity, View itemView) { @TargetApi(21)
public AppListItemController(final Activity activity, View itemView) {
super(itemView); super(itemView);
this.activity = activity; this.activity = activity;
installButton = (Button) itemView.findViewById(R.id.install); installButton = (ImageView) itemView.findViewById(R.id.install);
installButton.setOnClickListener(onInstallClicked); if (installButton != null) {
installButton.setOnClickListener(onInstallClicked);
if (Build.VERSION.SDK_INT >= 21) {
installButton.setOutlineProvider(new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
float density = activity.getResources().getDisplayMetrics().density;
// TODO: This is a bit hacky/hardcoded/too-specific to the particular icons we're using.
// This is because the default "download & install" and "downloaded & ready to install"
// icons are smaller than the "downloading progress" button. Hence, we can't just use
// the width/height of the view to calculate the outline size.
int xPadding = (int) (8 * density);
int yPadding = (int) (9 * density);
outline.setOval(xPadding, yPadding, installButton.getWidth() - xPadding, installButton.getHeight() - yPadding);
}
});
}
}
icon = (ImageView) itemView.findViewById(R.id.icon); icon = (ImageView) itemView.findViewById(R.id.icon);
name = (TextView) itemView.findViewById(R.id.app_name); name = (TextView) itemView.findViewById(R.id.app_name);
status = (TextView) itemView.findViewById(R.id.status); status = (TextView) itemView.findViewById(R.id.status);
installedVersion = (TextView) itemView.findViewById(R.id.installed_version);
ignoredStatus = (TextView) itemView.findViewById(R.id.ignored_status);
displayImageOptions = Utils.getImageLoadingOptions().build(); displayImageOptions = Utils.getImageLoadingOptions().build();
@ -58,7 +119,19 @@ public class AppListItemController extends RecyclerView.ViewHolder {
ImageLoader.getInstance().displayImage(app.iconUrl, icon, displayImageOptions); ImageLoader.getInstance().displayImage(app.iconUrl, icon, displayImageOptions);
Apk apkToInstall = ApkProvider.Helper.findApkFromAnyRepo(activity, app.packageName, app.suggestedVersionCode);
currentAppDownloadUrl = apkToInstall.getUrl();
final LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(activity.getApplicationContext());
broadcastManager.unregisterReceiver(onDownloadProgress);
broadcastManager.unregisterReceiver(onInstallAction);
broadcastManager.registerReceiver(onDownloadProgress, DownloaderService.getIntentFilter(currentAppDownloadUrl));
broadcastManager.registerReceiver(onInstallAction, Installer.getInstallIntentFilter(Uri.parse(currentAppDownloadUrl)));
configureStatusText(app); configureStatusText(app);
configureInstalledVersion(app);
configureIgnoredStatus(app);
configureInstallButton(app); configureInstallButton(app);
} }
@ -94,6 +167,51 @@ public class AppListItemController extends RecyclerView.ViewHolder {
} }
/**
* Shows the currently installed version name, and whether or not it is the recommended version.
* Binds to the {@link R.id#installed_version} {@link TextView}.
*/
private void configureInstalledVersion(@NonNull App app) {
if (installedVersion == null) {
return;
}
int res = (app.suggestedVersionCode == app.installedVersionCode)
? R.string.app_recommended_version_installed : R.string.app_version_x_installed;
installedVersion.setText(activity.getString(res, app.installedVersionName));
}
/**
* Shows whether the user has previously asked to ignore updates for this app entirely, or for a
* specific version of this app. Binds to the {@link R.id#ignored_status} {@link TextView}.
*/
private void configureIgnoredStatus(@NonNull App app) {
if (ignoredStatus == null) {
return;
}
AppPrefs prefs = app.getPrefs(activity);
if (prefs.ignoreAllUpdates) {
ignoredStatus.setText(activity.getString(R.string.installed_app__updates_ignored));
ignoredStatus.setVisibility(View.VISIBLE);
} else if (prefs.ignoreThisUpdate > 0 && prefs.ignoreThisUpdate == app.suggestedVersionCode) {
ignoredStatus.setText(activity.getString(R.string.installed_app__updates_ignored_for_suggested_version, app.getSuggestedVersionName()));
ignoredStatus.setVisibility(View.VISIBLE);
} else {
ignoredStatus.setVisibility(View.GONE);
}
}
private boolean isReadyToInstall(@NonNull App app) {
for (AppUpdateStatusManager.AppUpdateStatus appStatus : AppUpdateStatusManager.getInstance(activity).getByPackageName(app.packageName)) {
if (appStatus.status == AppUpdateStatusManager.Status.ReadyToInstall) {
return true;
}
}
return false;
}
/** /**
* The install button is shown when an app: * The install button is shown when an app:
* * Is compatible with the users device. * * Is compatible with the users device.
@ -107,16 +225,24 @@ public class AppListItemController extends RecyclerView.ViewHolder {
return; return;
} }
boolean installable = app.canAndWantToUpdate(activity) || !app.isInstalled(); if (isReadyToInstall(app)) {
boolean shouldAllow = app.compatible && !app.isFiltered(); installButton.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.ic_download_complete));
if (shouldAllow && installable) {
installButton.setVisibility(View.VISIBLE); installButton.setVisibility(View.VISIBLE);
// TODO: If in the downloading phase, then need to reflect that instead of this "download complete" icon.
} else { } else {
installButton.setVisibility(View.GONE); boolean installable = app.canAndWantToUpdate(activity) || !app.isInstalled();
boolean shouldAllow = app.compatible && !app.isFiltered();
if (shouldAllow && installable) {
installButton.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.ic_download));
installButton.setVisibility(View.VISIBLE);
} else {
installButton.setVisibility(View.GONE);
}
} }
} }
@SuppressWarnings("FieldCanBeLocal")
private final View.OnClickListener onAppClicked = new View.OnClickListener() { private final View.OnClickListener onAppClicked = new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
@ -136,6 +262,52 @@ public class AppListItemController extends RecyclerView.ViewHolder {
} }
}; };
private final BroadcastReceiver onDownloadProgress = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (installButton == null || currentApp == null || !TextUtils.equals(currentAppDownloadUrl, intent.getDataString())) {
return;
}
if (Downloader.ACTION_PROGRESS.equals(intent.getAction())) {
installButton.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.ic_download_progress));
int bytesRead = intent.getIntExtra(Downloader.EXTRA_BYTES_READ, 0);
int totalBytes = intent.getIntExtra(Downloader.EXTRA_TOTAL_BYTES, 100);
int progressAsDegrees = (int) (((float) bytesRead / totalBytes) * 360);
installButton.setImageLevel(progressAsDegrees);
} else if (Downloader.ACTION_COMPLETE.equals(intent.getAction())) {
installButton.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.ic_download_complete));
}
}
};
private final BroadcastReceiver onInstallAction = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (currentApp == null || installButton == null) {
return;
}
Apk apk = intent.getParcelableExtra(Installer.EXTRA_APK);
if (!TextUtils.equals(apk.packageName, currentApp.packageName)) {
return;
}
if (Installer.ACTION_INSTALL_STARTED.equals(intent.getAction())) {
installButton.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.ic_download_progress));
installButton.setImageLevel(0);
} else if (Installer.ACTION_INSTALL_COMPLETE.equals(intent.getAction())) {
installButton.setVisibility(View.GONE);
// TODO: It could've been a different version other than the current suggested version.
// In these cases, don't hide the button but rather set it back to the default install image.
} else if (Installer.ACTION_INSTALL_INTERRUPTED.equals(intent.getAction())) {
installButton.setImageDrawable(ContextCompat.getDrawable(activity, R.drawable.ic_download));
}
}
};
@SuppressWarnings("FieldCanBeLocal")
private final View.OnClickListener onInstallClicked = new View.OnClickListener() { private final View.OnClickListener onInstallClicked = new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
@ -143,7 +315,36 @@ public class AppListItemController extends RecyclerView.ViewHolder {
return; return;
} }
InstallManagerService.queue(activity, currentApp, ApkProvider.Helper.findApkFromAnyRepo(activity, currentApp.packageName, currentApp.suggestedVersionCode)); final Apk suggestedApk = ApkProvider.Helper.findApkFromAnyRepo(activity, currentApp.packageName, currentApp.suggestedVersionCode);
if (isReadyToInstall(currentApp)) {
File apkFilePath = ApkCache.getApkDownloadPath(activity, Uri.parse(suggestedApk.getUrl()));
Utils.debugLog(TAG, "skip download, we have already downloaded " + suggestedApk.getUrl() + " to " + apkFilePath);
// TODO: This seems like a bit of a hack. Is there a better way to do this by changing
// the Installer API so that we can ask it to install without having to get it to fire
// off an intent which we then listen for and action?
final LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(activity);
final BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
broadcastManager.unregisterReceiver(this);
if (Installer.ACTION_INSTALL_USER_INTERACTION.equals(intent.getAction())) {
PendingIntent pendingIntent = intent.getParcelableExtra(Installer.EXTRA_USER_INTERACTION_PI);
try {
pendingIntent.send();
} catch (PendingIntent.CanceledException ignored) { }
}
}
};
broadcastManager.registerReceiver(receiver, Installer.getInstallIntentFilter(Uri.parse(suggestedApk.getUrl())));
Installer installer = InstallerFactory.create(activity, suggestedApk);
installer.installPackage(Uri.parse(apkFilePath.toURI().toString()), Uri.parse(suggestedApk.getUrl()));
} else {
InstallManagerService.queue(activity, currentApp, suggestedApk);
}
} }
}; };
} }

View File

@ -139,7 +139,8 @@ public class CategoryTextWatcher implements TextWatcher {
if (Build.VERSION.SDK_INT >= 21) { if (Build.VERSION.SDK_INT >= 21) {
// For accessibility reasons, make this more clear to screen readers that the // For accessibility reasons, make this more clear to screen readers that the
// span we just added semantically represents a category. // span we just added semantically represents a category.
TtsSpan ttsSpan = new TtsSpan.TextBuilder(context.getString(R.string.category)).build(); CharSequence categoryName = textToSpannify.subSequence(0, colonIndex);
TtsSpan ttsSpan = new TtsSpan.TextBuilder(context.getString(R.string.tts_category_name, categoryName)).build();
textToSpannify.setSpan(ttsSpan, 0, 0, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); textToSpannify.setSpan(ttsSpan, 0, 0, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
} }
} }

View File

@ -0,0 +1,228 @@
package org.fdroid.fdroid.views.apps;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.os.Build;
import android.support.annotation.Nullable;
import android.support.v7.graphics.Palette;
import android.support.v7.widget.AppCompatImageView;
import android.util.AttributeSet;
import java.util.Random;
/**
* A feature image can have a {@link android.graphics.drawable.Drawable} or a {@link Palette}. If
* a Drawable is available, then it will draw that, otherwise it will attempt to fall back to the
* Palette you gave it. If a Palette is given, it will draw a series of triangles like so:
*
* +_----+----_+_----+----_+
* | \_ | _/ | \_ | _/ |
* | \_|_/ | \_|_/ |
* +_----+----_+_----+----_+
* | \_ | _/ | \_ | _/ |
* | \_|_/ | \_|_/ |
* +-----+-----+-----+-----+
*
* where each triangle is filled with one of two variations of the {@link Palette#getDominantColor(int)}
* that is chosen randomly. The randomness is first seeded with the colour that has been selected.
* This is so that if this repaints itself in the future, it will have the same unique pattern rather
* than picking a new random pattern each time.
*
* It is suggested that you obtain the Palette from the icon of an app.
*/
public class FeatureImage extends AppCompatImageView {
private static final int NUM_SQUARES_WIDE = 4;
private static final int NUM_SQUARES_HIGH = 2;
// Double, because there are two triangles per square.
private final Path[] triangles = new Path[NUM_SQUARES_HIGH * NUM_SQUARES_WIDE * 2];
@Nullable
private Paint[] trianglePaints;
private static final Paint WHITE_PAINT = new Paint();
static {
WHITE_PAINT.setColor(Color.WHITE);
WHITE_PAINT.setStyle(Paint.Style.FILL);
}
public FeatureImage(Context context) {
super(context);
}
public FeatureImage(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public FeatureImage(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/**
* Takes the {@link Palette#getDominantColor(int)} from the palette, dims it substantially, and
* then creates a second variation that is slightly dimmer still. These two colours are then
* randomly allocated to each triangle which is expected to be rendered.
*/
public void setPalette(@Nullable Palette palette) {
if (palette == null) {
trianglePaints = null;
return;
}
// It is easier to dull al colour in the HSV space, so convert to that then adjust the
// saturation down and the colour value down.
float[] hsv = new float[3];
Color.colorToHSV(palette.getDominantColor(Color.LTGRAY), hsv);
hsv[1] *= 0.5f;
hsv[2] *= 0.7f;
int colourOne = Color.HSVToColor(hsv);
hsv[2] *= 0.9f;
int colourTwo = Color.HSVToColor(hsv);
Paint paintOne = new Paint();
paintOne.setColor(colourOne);
paintOne.setAntiAlias(true);
paintOne.setStrokeWidth(2);
paintOne.setStyle(Paint.Style.FILL_AND_STROKE);
Paint paintTwo = new Paint();
paintTwo.setColor(colourTwo);
paintTwo.setAntiAlias(true);
paintTwo.setStrokeWidth(2);
paintTwo.setStyle(Paint.Style.FILL_AND_STROKE);
// Seed based on the colour, so that each time we try to render a feature image with the
// same colour, it will give the same pattern.
Random random = new Random(colourOne);
trianglePaints = new Paint[triangles.length];
for (int i = 0; i < trianglePaints.length; i++) {
trianglePaints[i] = random.nextBoolean() ? paintOne : paintTwo;
}
animateColourChange();
}
private int currentAlpha = 255;
private ValueAnimator alphaAnimator = null;
@TargetApi(11)
private void animateColourChange() {
if (Build.VERSION.SDK_INT < 11) {
return;
}
if (alphaAnimator == null) {
alphaAnimator = ValueAnimator.ofInt(0, 255);
} else {
alphaAnimator.cancel();
}
alphaAnimator = ValueAnimator.ofInt(0, 255).setDuration(150);
alphaAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
currentAlpha = (int) animation.getAnimatedValue();
invalidate();
}
});
currentAlpha = 0;
invalidate();
alphaAnimator.start();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
int triangleWidth = w / NUM_SQUARES_WIDE;
int triangleHeight = h / NUM_SQUARES_HIGH;
for (int x = 0; x < NUM_SQUARES_WIDE; x++) {
for (int y = 0; y < NUM_SQUARES_HIGH; y++) {
int startX = x * triangleWidth;
int startY = y * triangleHeight;
int endX = startX + triangleWidth;
int endY = startY + triangleHeight;
// Note that the order of these points need to go in a clockwise direction, or else
// the fill will not be applied properly.
Path firstTriangle;
Path secondTriangle;
// Alternate between two different ways to split a square into two triangles. This
// results in a nicer geometric pattern (see doc comments at top of class for more
// ASCII art of the expected outcome).
if (x % 2 == 0) {
// +_----+
// | \_ 1|
// |2 \_|
// +-----+
firstTriangle = createTriangle(new Point(startX, startY), new Point(endX, startY), new Point(endX, endY));
secondTriangle = createTriangle(new Point(startX, startY), new Point(endX, endY), new Point(startX, endY));
} else {
// +----_+
// |1 _/ |
// |_/ 2|
// +-----+
firstTriangle = createTriangle(new Point(startX, startY), new Point(endX, startY), new Point(startX, endY));
secondTriangle = createTriangle(new Point(startX, endY), new Point(endX, startY), new Point(endX, endY));
}
triangles[y * (NUM_SQUARES_WIDE * 2) + (x * 2)] = firstTriangle;
triangles[y * (NUM_SQUARES_WIDE * 2) + (x * 2) + 1] = secondTriangle;
}
}
}
/**
* First try to draw whatever image was given to this view. If that doesn't exist, try to draw
* a geometric pattern based on the palette that was given to us. If we haven't had a palette
* assigned to us (using {@link FeatureImage#setPalette(Palette)}) then clear the
* view by filling it with white.
*/
@Override
protected void onDraw(Canvas canvas) {
if (getDrawable() != null) {
super.onDraw(canvas);
} else if (trianglePaints != null) {
for (Paint paint : trianglePaints) {
paint.setAlpha(currentAlpha);
}
canvas.drawRect(0, 0, getWidth(), getHeight(), WHITE_PAINT);
for (int i = 0; i < triangles.length; i++) {
canvas.drawPath(triangles[i], trianglePaints[i]);
}
} else {
canvas.drawRect(0, 0, getWidth(), getHeight(), WHITE_PAINT);
}
}
/**
* This requires the three points to be in a sequence that traces out a triangle in clockwise
* fashion. This is required for the triangle to be filled correctly when drawing, otherwise
* it will end up black.
*/
private static Path createTriangle(Point start, Point middle, Point end) {
Path path = new Path();
path.setFillType(Path.FillType.EVEN_ODD);
path.moveTo(start.x, start.y);
path.lineTo(middle.x, middle.y);
path.lineTo(end.x, end.y);
path.close();
return path;
}
}

View File

@ -23,57 +23,59 @@ import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
import org.fdroid.fdroid.AppDetails; import org.fdroid.fdroid.AppDetails;
import org.fdroid.fdroid.AppDetails2; import org.fdroid.fdroid.AppDetails2;
import org.fdroid.fdroid.Preferences;
import org.fdroid.fdroid.R; import org.fdroid.fdroid.R;
import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.Utils;
import org.fdroid.fdroid.data.App; import org.fdroid.fdroid.data.App;
import org.fdroid.fdroid.views.apps.FeatureImage;
import java.util.Date;
/** /**
* The {@link AppCardController} can bind an app to several different layouts, as long as the layout * The {@link AppCardController} can bind an app to several different layouts, as long as the layout
* contains the following elements: * contains the following elements:
* + {@link R.id#icon} ({@link ImageView}, required) * + {@link R.id#icon} ({@link ImageView}, required)
* + {@link R.id#summary} ({@link TextView}, required) * + {@link R.id#summary} ({@link TextView}, required)
* + {@link R.id#new_tag} ({@link TextView}, optional)
* + {@link R.id#featured_image} ({@link ImageView}, optional) * + {@link R.id#featured_image} ({@link ImageView}, optional)
* + {@link R.id#status} ({@link TextView}, optional)
*/ */
public class AppCardController extends RecyclerView.ViewHolder implements ImageLoadingListener, View.OnClickListener { public class AppCardController extends RecyclerView.ViewHolder implements ImageLoadingListener, View.OnClickListener {
@NonNull @NonNull
private final ImageView icon; private final ImageView icon;
/**
* Text starting with the app name (in bold) followed by a short summary of the app.
*/
@NonNull @NonNull
private final TextView summary; private final TextView summary;
/**
* A little blue tag which says "New" to indicate an app was added to the repository recently.
*/
@Nullable @Nullable
private final TextView status; private final TextView newTag;
/**
* Wide and short image for branding the app. If it is not present in the metadata then F-Droid
* will draw some abstract art instead.
*/
@Nullable @Nullable
private final ImageView featuredImage; private final FeatureImage featuredImage;
@Nullable @Nullable
private App currentApp; private App currentApp;
private final Activity activity; private final Activity activity;
private final int defaultFeaturedImageColour;
private final DisplayImageOptions displayImageOptions; private final DisplayImageOptions displayImageOptions;
private final Date recentCuttoffDate;
public AppCardController(Activity activity, View itemView) { public AppCardController(Activity activity, View itemView) {
super(itemView); super(itemView);
this.activity = activity; this.activity = activity;
recentCuttoffDate = Preferences.get().calcMaxHistory();
icon = (ImageView) findViewAndEnsureNonNull(itemView, R.id.icon); icon = (ImageView) findViewAndEnsureNonNull(itemView, R.id.icon);
summary = (TextView) findViewAndEnsureNonNull(itemView, R.id.summary); summary = (TextView) findViewAndEnsureNonNull(itemView, R.id.summary);
featuredImage = (ImageView) itemView.findViewById(R.id.featured_image); featuredImage = (FeatureImage) itemView.findViewById(R.id.featured_image);
status = (TextView) itemView.findViewById(R.id.status); newTag = (TextView) itemView.findViewById(R.id.new_tag);
defaultFeaturedImageColour = activity.getResources().getColor(R.color.cardview_light_background);
displayImageOptions = Utils.getImageLoadingOptions().build(); displayImageOptions = Utils.getImageLoadingOptions().build();
itemView.setOnClickListener(this); itemView.setOnClickListener(this);
@ -100,20 +102,17 @@ public class AppCardController extends RecyclerView.ViewHolder implements ImageL
summary.setText(Utils.formatAppNameAndSummary(app.name, app.summary)); summary.setText(Utils.formatAppNameAndSummary(app.name, app.summary));
if (status != null) { if (newTag != null) {
if (app.added != null && app.added.after(recentCuttoffDate) && (app.lastUpdated == null || app.added.equals(app.lastUpdated))) { if (app.added != null && app.lastUpdated != null && app.added.equals(app.lastUpdated)) {
status.setText(activity.getString(R.string.category_Whats_New)); newTag.setVisibility(View.VISIBLE);
status.setVisibility(View.VISIBLE);
} else if (app.lastUpdated != null && app.lastUpdated.after(recentCuttoffDate)) {
status.setText(activity.getString(R.string.category_Recently_Updated));
status.setVisibility(View.VISIBLE);
} else { } else {
status.setVisibility(View.GONE); newTag.setVisibility(View.GONE);
} }
} }
if (featuredImage != null) { if (featuredImage != null) {
featuredImage.setBackgroundColor(defaultFeaturedImageColour); featuredImage.setPalette(null);
featuredImage.setImageDrawable(null);
} }
ImageLoader.getInstance().displayImage(app.iconUrl, icon, displayImageOptions, this); ImageLoader.getInstance().displayImage(app.iconUrl, icon, displayImageOptions, this);
@ -151,12 +150,11 @@ public class AppCardController extends RecyclerView.ViewHolder implements ImageL
@Override @Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
final ImageView image = featuredImage; if (featuredImage != null) {
if (image != null) {
new Palette.Builder(loadedImage).generate(new Palette.PaletteAsyncListener() { new Palette.Builder(loadedImage).generate(new Palette.PaletteAsyncListener() {
@Override @Override
public void onGenerated(Palette palette) { public void onGenerated(Palette palette) {
image.setBackgroundColor(palette.getDominantColor(defaultFeaturedImageColour)); featuredImage.setPalette(palette);
} }
}); });
} }

View File

@ -121,6 +121,7 @@ public class CategoryController extends RecyclerView.ViewHolder implements Loade
int numAppsInCategory = cursor.getInt(0); int numAppsInCategory = cursor.getInt(0);
viewAll.setVisibility(View.VISIBLE); viewAll.setVisibility(View.VISIBLE);
viewAll.setText(activity.getResources().getQuantityString(R.plurals.button_view_all_apps_in_category, numAppsInCategory, numAppsInCategory)); viewAll.setText(activity.getResources().getQuantityString(R.plurals.button_view_all_apps_in_category, numAppsInCategory, numAppsInCategory));
viewAll.setContentDescription(activity.getResources().getQuantityString(R.plurals.tts_view_all_in_category, numAppsInCategory, numAppsInCategory, currentCategory));
} }
} }

View File

@ -1,8 +1,6 @@
package org.fdroid.fdroid.views.fragments; package org.fdroid.fdroid.views.fragments;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor; import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
@ -143,12 +141,10 @@ public abstract class AppListFragment extends ListFragment implements
* be bad. * be bad.
*/ */
private boolean updateEmptyRepos() { private boolean updateEmptyRepos() {
final String triedEmptyUpdate = "triedEmptyUpdate"; Preferences prefs = Preferences.get();
SharedPreferences prefs = getActivity().getPreferences(Context.MODE_PRIVATE); if (!prefs.hasTriedEmptyUpdate()) {
boolean hasTriedEmptyUpdate = prefs.getBoolean(triedEmptyUpdate, false);
if (!hasTriedEmptyUpdate) {
Utils.debugLog(TAG, "Empty app list, and we haven't done an update yet. Forcing repo update."); Utils.debugLog(TAG, "Empty app list, and we haven't done an update yet. Forcing repo update.");
prefs.edit().putBoolean(triedEmptyUpdate, true).apply(); prefs.setTriedEmptyUpdate(true);
UpdateService.updateNow(getActivity()); UpdateService.updateNow(getActivity());
return true; return true;
} }

View File

@ -0,0 +1,143 @@
/*
* Copyright (C) 2010-12 Ciaran Gultnieks, ciaran@ciarang.com
* Copyright (C) 2009 Roberto Jacinto, roberto.jacinto@caixamagica.pt
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.fdroid.fdroid.views.installed;
import android.app.Activity;
import android.database.Cursor;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.ViewGroup;
import org.fdroid.fdroid.FDroidApp;
import org.fdroid.fdroid.R;
import org.fdroid.fdroid.data.App;
import org.fdroid.fdroid.data.AppProvider;
import org.fdroid.fdroid.data.Schema;
import org.fdroid.fdroid.views.apps.AppListItemController;
public class InstalledAppsActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor> {
private InstalledAppListAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
((FDroidApp) getApplication()).applyTheme(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.installed_apps_layout);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
toolbar.setTitle(getString(R.string.installed_apps__activity_title));
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
adapter = new InstalledAppListAdapter(this);
RecyclerView appList = (RecyclerView) findViewById(R.id.app_list);
appList.setHasFixedSize(true);
appList.setLayoutManager(new LinearLayoutManager(this));
appList.setAdapter(adapter);
}
@Override
protected void onResume() {
super.onResume();
// Starts a new or restarts an existing Loader in this manager
getSupportLoaderManager().restartLoader(0, null, this);
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new CursorLoader(
this,
AppProvider.getInstalledUri(),
Schema.AppMetadataTable.Cols.ALL,
null, null, null);
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
adapter.setApps(cursor);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
adapter.setApps(null);
}
static class InstalledAppListAdapter extends RecyclerView.Adapter<AppListItemController> {
private final Activity activity;
@Nullable
private Cursor cursor;
InstalledAppListAdapter(Activity activity) {
this.activity = activity;
setHasStableIds(true);
}
@Override
public long getItemId(int position) {
if (cursor == null) {
return 0;
}
cursor.moveToPosition(position);
return cursor.getLong(cursor.getColumnIndex(Schema.AppMetadataTable.Cols.ROW_ID));
}
@Override
public AppListItemController onCreateViewHolder(ViewGroup parent, int viewType) {
View view = activity.getLayoutInflater().inflate(R.layout.installed_app_list_item, parent, false);
return new AppListItemController(activity, view);
}
@Override
public void onBindViewHolder(AppListItemController holder, int position) {
if (cursor == null) {
return;
}
cursor.moveToPosition(position);
holder.bindModel(new App(cursor));
}
@Override
public int getItemCount() {
return cursor == null ? 0 : cursor.getCount();
}
public void setApps(@Nullable Cursor cursor) {
this.cursor = cursor;
notifyDataSetChanged();
}
}
}

View File

@ -1,7 +1,9 @@
package org.fdroid.fdroid.views.main; package org.fdroid.fdroid.views.main;
import android.content.Intent;
import android.database.Cursor; import android.database.Cursor;
import android.os.Bundle; import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.LoaderManager; import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader; import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader; import android.support.v4.content.Loader;
@ -14,6 +16,7 @@ import android.widget.FrameLayout;
import org.fdroid.fdroid.R; import org.fdroid.fdroid.R;
import org.fdroid.fdroid.data.CategoryProvider; import org.fdroid.fdroid.data.CategoryProvider;
import org.fdroid.fdroid.data.Schema; import org.fdroid.fdroid.data.Schema;
import org.fdroid.fdroid.views.apps.AppListActivity;
import org.fdroid.fdroid.views.categories.CategoryAdapter; import org.fdroid.fdroid.views.categories.CategoryAdapter;
/** /**
@ -28,7 +31,7 @@ class CategoriesViewBinder implements LoaderManager.LoaderCallbacks<Cursor> {
private final CategoryAdapter categoryAdapter; private final CategoryAdapter categoryAdapter;
private final AppCompatActivity activity; private final AppCompatActivity activity;
CategoriesViewBinder(AppCompatActivity activity, FrameLayout parent) { CategoriesViewBinder(final AppCompatActivity activity, FrameLayout parent) {
this.activity = activity; this.activity = activity;
View categoriesView = activity.getLayoutInflater().inflate(R.layout.main_tab_categories, parent, true); View categoriesView = activity.getLayoutInflater().inflate(R.layout.main_tab_categories, parent, true);
@ -40,6 +43,14 @@ class CategoriesViewBinder implements LoaderManager.LoaderCallbacks<Cursor> {
categoriesList.setLayoutManager(new LinearLayoutManager(activity)); categoriesList.setLayoutManager(new LinearLayoutManager(activity));
categoriesList.setAdapter(categoryAdapter); categoriesList.setAdapter(categoryAdapter);
FloatingActionButton searchFab = (FloatingActionButton) categoriesView.findViewById(R.id.btn_search);
searchFab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
activity.startActivity(new Intent(activity, AppListActivity.class));
}
});
activity.getSupportLoaderManager().initLoader(LOADER_ID, null, this); activity.getSupportLoaderManager().initLoader(LOADER_ID, null, this);
} }

View File

@ -1,15 +1,32 @@
package org.fdroid.fdroid.views.main; package org.fdroid.fdroid.views.main;
import android.app.SearchManager;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.design.widget.BottomNavigationView; import android.support.design.widget.BottomNavigationView;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
import android.text.TextUtils;
import android.view.MenuItem; import android.view.MenuItem;
import android.widget.Toast;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import org.fdroid.fdroid.AppDetails;
import org.fdroid.fdroid.AppDetails2;
import org.fdroid.fdroid.FDroidApp;
import org.fdroid.fdroid.NfcHelper;
import org.fdroid.fdroid.Preferences;
import org.fdroid.fdroid.R; import org.fdroid.fdroid.R;
import org.fdroid.fdroid.UpdateService;
import org.fdroid.fdroid.Utils;
import org.fdroid.fdroid.compat.UriCompat;
import org.fdroid.fdroid.data.NewRepoConfig;
import org.fdroid.fdroid.views.ManageReposActivity;
import org.fdroid.fdroid.views.apps.AppListActivity;
import org.fdroid.fdroid.views.swap.SwapWorkflowActivity;
/** /**
* Main view shown to users upon starting F-Droid. * Main view shown to users upon starting F-Droid.
@ -27,7 +44,18 @@ import org.fdroid.fdroid.R;
*/ */
public class MainActivity extends AppCompatActivity implements BottomNavigationView.OnNavigationItemSelectedListener { public class MainActivity extends AppCompatActivity implements BottomNavigationView.OnNavigationItemSelectedListener {
private static final String TAG = "MainActivity";
public static final String EXTRA_VIEW_MY_APPS = "org.fdroid.fdroid.views.main.MainActivity.VIEW_MY_APPS";
private static final String ADD_REPO_INTENT_HANDLED = "addRepoIntentHandled";
private static final String ACTION_ADD_REPO = "org.fdroid.fdroid.MainActivity.ACTION_ADD_REPO";
private static final int REQUEST_SWAP = 3;
private RecyclerView pager; private RecyclerView pager;
private MainViewAdapter adapter;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@ -35,13 +63,67 @@ public class MainActivity extends AppCompatActivity implements BottomNavigationV
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
adapter = new MainViewAdapter(this);
pager = (RecyclerView) findViewById(R.id.main_view_pager); pager = (RecyclerView) findViewById(R.id.main_view_pager);
pager.setHasFixedSize(true); pager.setHasFixedSize(true);
pager.setLayoutManager(new NonScrollingHorizontalLayoutManager(this)); pager.setLayoutManager(new NonScrollingHorizontalLayoutManager(this));
pager.setAdapter(new MainViewAdapter(this)); pager.setAdapter(adapter);
BottomNavigationView bottomNavigation = (BottomNavigationView) findViewById(R.id.bottom_navigation); BottomNavigationView bottomNavigation = (BottomNavigationView) findViewById(R.id.bottom_navigation);
bottomNavigation.setOnNavigationItemSelectedListener(this); bottomNavigation.setOnNavigationItemSelectedListener(this);
initialRepoUpdateIfRequired();
Intent intent = getIntent();
handleSearchOrAppViewIntent(intent);
}
/**
* The first time the app is run, we will have an empty app list. To deal with this, we will
* attempt to update with the default repo. However, if we have tried this at least once, then
* don't try to do it automatically again.
*/
private void initialRepoUpdateIfRequired() {
Preferences prefs = Preferences.get();
if (!prefs.hasTriedEmptyUpdate()) {
Utils.debugLog(TAG, "We haven't done an update yet. Forcing repo update.");
prefs.setTriedEmptyUpdate(true);
UpdateService.updateNow(this);
}
}
@Override
protected void onResume() {
super.onResume();
FDroidApp.checkStartTor(this);
if (getIntent().hasExtra(EXTRA_VIEW_MY_APPS)) {
getIntent().removeExtra(EXTRA_VIEW_MY_APPS);
pager.scrollToPosition(adapter.adapterPositionFromItemId(R.id.my_apps));
}
// AppDetails 2 and RepoDetailsActivity set different NFC actions, so reset here
NfcHelper.setAndroidBeam(this, getApplication().getPackageName());
checkForAddRepoIntent(getIntent());
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
handleSearchOrAppViewIntent(intent);
// This is called here as well as onResume(), because onNewIntent() is not called the first
// time the activity is created. An alternative option to make sure that the add repo intent
// is always handled is to call setIntent(intent) here. However, after this good read:
// http://stackoverflow.com/a/7749347 it seems that adding a repo is not really more
// important than the original intent which caused the activity to start (even though it
// could technically have been an add repo intent itself).
// The end result is that this method will be called twice for one add repo intent. Once
// here and once in onResume(). However, the method deals with this by ensuring it only
// handles the same intent once.
checkForAddRepoIntent(intent);
} }
@Override @Override
@ -50,6 +132,131 @@ public class MainActivity extends AppCompatActivity implements BottomNavigationV
return true; return true;
} }
private void handleSearchOrAppViewIntent(Intent intent) {
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
performSearch(query);
return;
}
final Uri data = intent.getData();
if (data == null) {
return;
}
final String scheme = data.getScheme();
final String path = data.getPath();
String packageName = null;
String query = null;
if (data.isHierarchical()) {
final String host = data.getHost();
if (host == null) {
return;
}
switch (host) {
case "f-droid.org":
if (path.startsWith("/repository/browse")) {
// http://f-droid.org/repository/browse?fdfilter=search+query
query = UriCompat.getQueryParameter(data, "fdfilter");
// http://f-droid.org/repository/browse?fdid=packageName
packageName = UriCompat.getQueryParameter(data, "fdid");
} else if (path.startsWith("/app")) {
// http://f-droid.org/app/packageName
packageName = data.getLastPathSegment();
if ("app".equals(packageName)) {
packageName = null;
}
}
break;
case "details":
// market://details?id=app.id
packageName = UriCompat.getQueryParameter(data, "id");
break;
case "search":
// market://search?q=query
query = UriCompat.getQueryParameter(data, "q");
break;
case "play.google.com":
if (path.startsWith("/store/apps/details")) {
// http://play.google.com/store/apps/details?id=app.id
packageName = UriCompat.getQueryParameter(data, "id");
} else if (path.startsWith("/store/search")) {
// http://play.google.com/store/search?q=foo
query = UriCompat.getQueryParameter(data, "q");
}
break;
case "apps":
case "amazon.com":
case "www.amazon.com":
// amzn://apps/android?p=app.id
// http://amazon.com/gp/mas/dl/android?s=app.id
packageName = UriCompat.getQueryParameter(data, "p");
query = UriCompat.getQueryParameter(data, "s");
break;
}
} else if ("fdroid.app".equals(scheme)) {
// fdroid.app:app.id
packageName = data.getSchemeSpecificPart();
} else if ("fdroid.search".equals(scheme)) {
// fdroid.search:query
query = data.getSchemeSpecificPart();
}
if (!TextUtils.isEmpty(query)) {
// an old format for querying via packageName
if (query.startsWith("pname:")) {
packageName = query.split(":")[1];
}
// sometimes, search URLs include pub: or other things before the query string
if (query.contains(":")) {
query = query.split(":")[1];
}
}
if (!TextUtils.isEmpty(packageName)) {
Utils.debugLog(TAG, "FDroid launched via app link for '" + packageName + "'");
Intent intentToInvoke = new Intent(this, AppDetails2.class);
intentToInvoke.putExtra(AppDetails.EXTRA_APPID, packageName);
startActivity(intentToInvoke);
finish();
} else if (!TextUtils.isEmpty(query)) {
Utils.debugLog(TAG, "FDroid launched via search link for '" + query + "'");
performSearch(query);
}
}
/**
* Initiates the {@link AppListActivity} with the relevant search terms passed in via the query arg.
*/
private void performSearch(String query) {
Intent searchIntent = new Intent(this, AppListActivity.class);
searchIntent.putExtra(AppListActivity.EXTRA_SEARCH_TERMS, query);
startActivity(searchIntent);
}
private void checkForAddRepoIntent(Intent intent) {
// Don't handle the intent after coming back to this view (e.g. after hitting the back button)
// http://stackoverflow.com/a/14820849
if (!intent.hasExtra(ADD_REPO_INTENT_HANDLED)) {
intent.putExtra(ADD_REPO_INTENT_HANDLED, true);
NewRepoConfig parser = new NewRepoConfig(this, intent);
if (parser.isValidRepo()) {
if (parser.isFromSwap()) {
Intent confirmIntent = new Intent(this, SwapWorkflowActivity.class);
confirmIntent.putExtra(SwapWorkflowActivity.EXTRA_CONFIRM, true);
confirmIntent.setData(intent.getData());
startActivityForResult(confirmIntent, REQUEST_SWAP);
} else {
startActivity(new Intent(ACTION_ADD_REPO, intent.getData(), this, ManageReposActivity.class));
}
} else if (parser.getErrorMessage() != null) {
Toast.makeText(this, parser.getErrorMessage(), Toast.LENGTH_LONG).show();
}
}
}
private static class NonScrollingHorizontalLayoutManager extends LinearLayoutManager { private static class NonScrollingHorizontalLayoutManager extends LinearLayoutManager {
NonScrollingHorizontalLayoutManager(Context context) { NonScrollingHorizontalLayoutManager(Context context) {
super(context, LinearLayoutManager.HORIZONTAL, false); super(context, LinearLayoutManager.HORIZONTAL, false);

View File

@ -41,6 +41,30 @@ class MainViewAdapter extends RecyclerView.Adapter<MainViewController> {
@Override @Override
public MainViewController onCreateViewHolder(ViewGroup parent, int viewType) { public MainViewController onCreateViewHolder(ViewGroup parent, int viewType) {
MainViewController holder = createEmptyView();
switch (viewType) {
case R.id.whats_new:
holder.bindWhatsNewView();
break;
case R.id.categories:
holder.bindCategoriesView();
break;
case R.id.nearby:
holder.bindSwapView();
break;
case R.id.my_apps:
holder.bindMyApps();
break;
case R.id.settings:
holder.bindSettingsView();
break;
default:
throw new IllegalStateException("Unknown view type " + viewType);
}
return holder;
}
private MainViewController createEmptyView() {
FrameLayout frame = new FrameLayout(activity); FrameLayout frame = new FrameLayout(activity);
frame.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); frame.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
return new MainViewController(activity, frame); return new MainViewController(activity, frame);
@ -48,20 +72,10 @@ class MainViewAdapter extends RecyclerView.Adapter<MainViewController> {
@Override @Override
public void onBindViewHolder(MainViewController holder, int position) { public void onBindViewHolder(MainViewController holder, int position) {
long menuId = getItemId(position); // The binding happens in onCreateViewHolder. This is because we never have more than one of
if (menuId == R.id.whats_new) { // each type of view in this main activity. Therefore, there is no benefit to re-binding new
holder.bindWhatsNewView(); // data each time we navigate back to an item, as the recycler view will just use the one we
} else if (menuId == R.id.categories) { // created earlier.
holder.bindCategoriesView();
} else if (menuId == R.id.nearby) {
holder.bindSwapView();
} else if (menuId == R.id.my_apps) {
holder.bindMyApps();
} else if (menuId == R.id.settings) {
holder.bindSettingsView();
} else {
holder.clearViews();
}
} }
@Override @Override
@ -69,6 +83,11 @@ class MainViewAdapter extends RecyclerView.Adapter<MainViewController> {
return positionToId.size(); return positionToId.size();
} }
@Override
public int getItemViewType(int position) {
return positionToId.get(position);
}
// The RecyclerViewPager and the BottomNavigationView both use menu item IDs to identify pages. // The RecyclerViewPager and the BottomNavigationView both use menu item IDs to identify pages.
@Override @Override
public long getItemId(int position) { public long getItemId(int position) {

View File

@ -30,10 +30,6 @@ class MainViewController extends RecyclerView.ViewHolder {
this.frame = frame; this.frame = frame;
} }
public void clearViews() {
frame.removeAllViews();
}
/** /**
* @see WhatsNewViewBinder * @see WhatsNewViewBinder
*/ */

View File

@ -1,7 +1,9 @@
package org.fdroid.fdroid.views.main; package org.fdroid.fdroid.views.main;
import android.content.Intent;
import android.database.Cursor; import android.database.Cursor;
import android.os.Bundle; import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.LoaderManager; import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader; import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader; import android.support.v4.content.Loader;
@ -16,6 +18,7 @@ import org.fdroid.fdroid.R;
import org.fdroid.fdroid.UpdateService; import org.fdroid.fdroid.UpdateService;
import org.fdroid.fdroid.data.AppProvider; import org.fdroid.fdroid.data.AppProvider;
import org.fdroid.fdroid.data.Schema; import org.fdroid.fdroid.data.Schema;
import org.fdroid.fdroid.views.apps.AppListActivity;
import org.fdroid.fdroid.views.whatsnew.WhatsNewAdapter; import org.fdroid.fdroid.views.whatsnew.WhatsNewAdapter;
/** /**
@ -52,6 +55,14 @@ class WhatsNewViewBinder implements LoaderManager.LoaderCallbacks<Cursor> {
} }
}); });
FloatingActionButton searchFab = (FloatingActionButton) whatsNewView.findViewById(R.id.btn_search);
searchFab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
activity.startActivity(new Intent(activity, AppListActivity.class));
}
});
activity.getSupportLoaderManager().initLoader(LOADER_ID, null, this); activity.getSupportLoaderManager().initLoader(LOADER_ID, null, this);
} }

View File

@ -254,7 +254,7 @@ public class SwapWorkflowActivity extends AppCompatActivity {
} }
private void promptToSetupWifiAP() { private void promptToSetupWifiAP() {
WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE); WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
WifiApControl ap = WifiApControl.getInstance(this); WifiApControl ap = WifiApControl.getInstance(this);
wifiManager.setWifiEnabled(false); wifiManager.setWifiEnabled(false);
if (!ap.enable()) { if (!ap.enable()) {

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="4dp" />
<solid android:color="#ffeff4f9" />
</shape>

View File

@ -1,64 +1,96 @@
<vector android:height="32dp" android:viewportHeight="129.34668" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:viewportWidth="400.0" android:width="100dp" xmlns:android="http://schemas.android.com/apk/res/android"> android:width="426dp"
<path android:fillAlpha="1" android:fillColor="#666666" android:height="137dp"
android:pathData="M35.43,0L364.57,0A35.43,35.43 48.94,0 1,400 35.43L400,93.91A35.43,35.43 49.14,0 1,364.57 129.35L35.43,129.35A35.43,35.43 0,0 1,-0 93.91L-0,35.43A35.43,35.43 0,0 1,35.43 0z" android:viewportWidth="400.0"
android:strokeColor="#00000000" android:strokeWidth="20"/> android:viewportHeight="129.34668">
<path android:fillAlpha="1" android:fillColor="#f2f2f2" <path
android:pathData="M36.65,2.13L363.35,2.13A33.75,33.75 46.31,0 1,397.1 35.88L397.1,93.47A33.75,33.75 46.31,0 1,363.35 127.22L36.65,127.22A33.75,33.75 0,0 1,2.9 93.47L2.9,35.88A33.75,33.75 0,0 1,36.65 2.13z" android:pathData="M36.88,0L363.12,0A36.88,36.88 0,0 1,400 36.88L400,92.47A36.88,36.88 0,0 1,363.12 129.35L36.88,129.35A36.88,36.88 0,0 1,0 92.47L0,36.88A36.88,36.88 0,0 1,36.88 0z"
android:strokeColor="#00000000" android:strokeWidth="20"/> android:strokeLineCap="round"
<path android:fillAlpha="1" android:fillColor="#ffffff" android:fillAlpha="1"
android:pathData="m69.87,20.24c-7.37,0.02 -14.73,1.83 -21.25,5.27 -6.05,3.17 -11.38,7.7 -15.49,13.16 -3.61,4.78 -6.29,10.28 -7.8,16.08 -1.79,6.83 -1.99,14.08 -0.58,21 0.82,4.05 2.19,7.98 4.06,11.66 2.72,5.37 6.5,10.19 11.06,14.12 4.41,3.81 9.54,6.78 15.06,8.69 5.19,1.8 10.71,2.66 16.2,2.53 5.87,-0.13 11.72,-1.38 17.12,-3.69 6.12,-2.61 11.66,-6.57 16.12,-11.5 4.21,-4.64 7.47,-10.15 9.49,-16.08 1.78,-5.2 2.61,-10.71 2.46,-16.2 -0.15,-5.72 -1.36,-11.41 -3.58,-16.68 -2.62,-6.22 -6.61,-11.86 -11.62,-16.38 -4.46,-4.04 -9.71,-7.21 -15.38,-9.24 -5.07,-1.83 -10.47,-2.75 -15.86,-2.72z" android:strokeColor="#00000000"
android:strokeColor="#00000000" android:strokeWidth="6.7312851"/> android:fillColor="#666666"
<path android:fillAlpha="1" android:fillColor="#ffffff" android:strokeWidth="1.87500012"
android:pathData="M189.48,56.05m0,0a<30>,<2C> <20>,1 1,0 0a<30>,<2C> <20>,1 1,0 0" android:strokeLineJoin="round"
android:strokeColor="#00000000" android:strokeWidth="6.82086611"/> android:strokeAlpha="1"/>
<path android:fillAlpha="1" android:fillColor="#ffffff" <path
android:pathData="m268.03,55.9 l-3.15,8.46c-1.8,-0.84 -3.42,-1.47 -4.86,-1.89 -1.44,-0.42 -2.91,-0.63 -4.41,-0.63 -3.96,0 -7.35,1.35 -10.17,4.05 -2.64,2.76 -3.96,6.18 -3.96,10.26 0,4.08 1.3,7.44 3.91,10.07 2.61,2.64 5.95,3.96 10.03,3.96 2.88,0 6,-0.99 9.36,-2.97l3.51,7.83 -2.97,1.62c-3.42,1.62 -7.2,2.43 -11.34,2.43 -6.48,0 -11.89,-2.17 -16.24,-6.52 -4.35,-4.35 -6.52,-9.79 -6.52,-16.33 0,-6.66 2.26,-12.22 6.79,-16.69 4.53,-4.47 10.18,-6.7 16.96,-6.7 4.56,0 8.91,1.02 13.04,3.06z" android:strokeColor="#00000000"/> android:pathData="M37.78,2.13L362.22,2.13A34.87,34.87 0,0 1,397.1 37L397.1,92.34A34.87,34.87 0,0 1,362.22 127.22L37.78,127.22A34.87,34.87 0,0 1,2.9 92.34L2.9,37A34.87,34.87 0,0 1,37.78 2.13z"
<path android:fillAlpha="1" android:fillColor="#ffffff" android:strokeLineCap="round"
android:pathData="m270.63,75.97c0,-6.6 2.26,-12.1 6.79,-16.51 4.53,-4.41 10.15,-6.61 16.87,-6.61 6.72,0 12.34,2.17 16.87,6.52 4.53,4.35 6.79,9.88 6.79,16.6 0,6.6 -2.25,12.1 -6.75,16.51 -4.5,4.41 -10.14,6.61 -16.91,6.61 -6.9,0 -12.56,-2.17 -17,-6.52 -4.44,-4.35 -6.66,-9.88 -6.66,-16.6zM294.29,61.48c-3.96,0 -7.27,1.38 -9.94,4.14 -2.67,2.76 -4,6.21 -4,10.35 0,2.16 0.3,4.05 0.9,5.67 0.6,1.62 1.62,3.18 3.06,4.68 2.64,2.7 5.97,4.05 9.99,4.05 3.96,0 7.27,-1.36 9.94,-4.09 2.67,-2.73 4,-6.16 4,-10.3 0,-2.16 -0.3,-4.05 -0.9,-5.67 -0.6,-1.62 -1.62,-3.18 -3.06,-4.68 -2.64,-2.76 -5.97,-4.14 -9.99,-4.14z" android:strokeColor="#00000000"/> android:fillAlpha="1"
<path android:fillAlpha="1" android:fillColor="#ffffff" android:strokeColor="#00000000"
android:pathData="m323.46,98.76 l0,-43.72 9.99,0 0,43.72z" android:strokeColor="#00000000"/> android:fillColor="#f2f2f2"
<path android:fillAlpha="1" android:fillColor="#ffffff" android:strokeWidth="1.87500012"
android:pathData="m322.11,44.86c0,-1.62 0.6,-3.03 1.8,-4.23 1.32,-1.14 2.82,-1.71 4.5,-1.71 1.86,0 3.4,0.55 4.63,1.66 1.23,1.11 1.84,2.53 1.84,4.27 0,1.56 -0.63,2.97 -1.89,4.23 -1.32,1.14 -2.85,1.71 -4.59,1.71 -1.68,0 -3.15,-0.55 -4.41,-1.66 -1.26,-1.11 -1.89,-2.53 -1.89,-4.27z" android:strokeColor="#00000000"/> android:strokeLineJoin="round"
<path android:fillAlpha="1" android:fillColor="#ffffff" android:strokeAlpha="1"/>
android:pathData="m340.75,98.76 l0,-43.72 9.99,0 0,6.75c3.24,-5.4 7.92,-8.1 14.03,-8.1 5.82,0 10.07,2.25 12.77,6.75 1.32,2.28 1.98,5.82 1.98,10.61l0,27.71 -9.99,0 0,-25.19 -0.27,-4.5c-0.24,-1.98 -1.2,-3.57 -2.88,-4.77 -1.56,-1.26 -3.39,-1.89 -5.49,-1.89 -3,0 -5.49,1.11 -7.47,3.33 -1.8,2.16 -2.7,5.22 -2.7,9.18l0,23.84z" android:strokeColor="#00000000"/> <path
<path android:fillAlpha="1" android:fillColor="#ffffff" android:pathData="M69.18,64.67m-46.16,0a46.16,46.16 0,1 1,92.32 0a46.16,46.16 0,1 1,-92.32 0"
android:pathData="m129.22,98.76 l0,-64.86 5.13,0 0,64.86z" android:strokeColor="#00000000"/> android:strokeLineCap="round"
<path android:fillAlpha="1" android:fillColor="#ffffff" android:fillAlpha="1"
android:pathData="m144.89,98.76 l0,-43.72 5.22,0 0,43.72z" android:strokeColor="#00000000"/> android:strokeColor="#00000000"
<path android:fillAlpha="1" android:fillColor="#ffffff" android:fillColor="#b3b3b3"
android:pathData="m156.86,58.88 l0,-4.5 5.94,0 0,-12.24 5.22,0 0,12.24 12.14,0 0,4.5 -12.14,0 0,25.82c0,2.28 0.06,3.63 0.18,4.05 0.36,3.84 2.61,5.76 6.75,5.76 1.68,0 3.27,-0.33 4.77,-0.99l1.26,4.59c-2.64,0.66 -4.92,0.99 -6.84,0.99 -3.96,0 -7.02,-1.38 -9.18,-4.14 -0.84,-1.14 -1.44,-2.34 -1.8,-3.6 -0.12,-0.78 -0.21,-1.62 -0.27,-2.52 -0.06,-0.9 -0.09,-2.34 -0.09,-4.32l0,-25.64z" android:strokeColor="#00000000"/> android:strokeWidth="1.87500012"
<path android:fillAlpha="1" android:fillColor="#ffffff" android:strokeLineJoin="round"
android:pathData="m225.59,77.77 l-37.96,0c0,2.52 0.42,4.78 1.26,6.79 0.84,2.01 2.1,3.85 3.78,5.53 3.12,3 7.02,4.5 11.7,4.5 7.2,0 12.42,-3.51 15.65,-10.53l4.77,2.43c-4.14,8.4 -10.92,12.6 -20.33,12.6 -6.66,0 -12.04,-2.1 -16.15,-6.3 -4.11,-4.2 -6.16,-9.72 -6.16,-16.56 0,-6.84 2.08,-12.47 6.25,-16.91 4.17,-4.44 9.49,-6.66 15.97,-6.66 6.06,0 11.13,2.08 15.2,6.25 4.08,4.17 6.12,9.43 6.12,15.79l0,1.26zM204.27,57.17c-4.56,0 -8.26,1.38 -11.11,4.14 -2.85,2.76 -4.72,6.75 -5.62,11.96l32.48,0c0,-4.8 -1.48,-8.68 -4.45,-11.65 -2.97,-2.97 -6.73,-4.45 -11.29,-4.45z" android:strokeColor="#00000000"/> android:strokeAlpha="1"/>
<path android:fillAlpha="1" android:fillColor="#ffffff" <path
android:pathData="m141.37,44.86c0,-1.62 0.6,-3.03 1.8,-4.23 1.32,-1.14 2.82,-1.71 4.5,-1.71 1.86,0 3.4,0.55 4.63,1.66 1.23,1.11 1.84,2.53 1.84,4.27 0,1.56 -0.63,2.97 -1.89,4.23 -1.32,1.14 -2.85,1.71 -4.59,1.71 -1.68,0 -3.15,-0.55 -4.41,-1.66 -1.26,-1.11 -1.89,-2.53 -1.89,-4.27z" android:strokeColor="#00000000"/> android:pathData="m44.58,93.88 l5.01,-19.04 -7.11,2.67 2.02,-7.61 7.08,-2.62 8.39,-31.8 18.23,-0.01 -6.38,24.31 8.19,-3.09 -2,7.62 -8.18,3.04 -4.06,15.38h30.12l-2.94,11.16z"
<path android:fillAlpha="1" android:fillColor="#b3b3b3" android:strokeLineCap="butt"
android:pathData="m67.96,18.33c-7.37,0.02 -14.73,1.83 -21.25,5.27 -6.05,3.17 -11.38,7.7 -15.49,13.16 -3.61,4.78 -6.29,10.28 -7.8,16.08 -1.79,6.83 -1.99,14.08 -0.58,21 0.82,4.05 2.19,7.98 4.06,11.66 2.72,5.37 6.5,10.19 11.06,14.12 4.41,3.81 9.54,6.78 15.06,8.69 5.19,1.8 10.71,2.66 16.2,2.53 5.87,-0.13 11.72,-1.38 17.12,-3.69 6.12,-2.61 11.66,-6.57 16.12,-11.5 4.21,-4.64 7.47,-10.15 9.49,-16.08 1.78,-5.2 2.61,-10.71 2.46,-16.2 -0.15,-5.72 -1.36,-11.41 -3.58,-16.68 -2.62,-6.22 -6.61,-11.86 -11.62,-16.38 -4.46,-4.04 -9.71,-7.21 -15.38,-9.24 -5.07,-1.83 -10.47,-2.75 -15.86,-2.72z" android:strokeColor="#00000000"
android:strokeColor="#00000000" android:strokeWidth="6.7312851"/> android:fillColor="#ffffff"
<path android:fillAlpha="1" android:fillColor="#808080" android:strokeWidth="0.93750006"
android:pathData="M187.58,54.15m0,0a<30>,<2C> <20>,1 1,0 0a<30>,<2C> <20>,1 1,0 0" android:strokeLineJoin="miter"
android:strokeColor="#00000000" android:strokeWidth="6.82086611"/> android:strokeAlpha="1"/>
<path android:fillAlpha="1" android:fillColor="#ffffff" <path
android:pathData="m59.98,32.27 l-8.39,31.8 -7.08,2.62 -2.02,7.61 7.11,-2.67 -5.01,19.04 48.37,0 2.94,-11.16 -30.12,0 4.06,-15.38 8.18,-3.04 2,-7.62 -8.19,3.09 6.38,-24.31z" android:pathData="m266.13,54.09 l-3.15,8.46c-1.8,-0.84 -3.42,-1.47 -4.86,-1.89 -1.44,-0.42 -2.91,-0.63 -4.41,-0.63 -3.96,0 -7.35,1.35 -10.17,4.05 -2.64,2.76 -3.96,6.18 -3.96,10.26 0,4.08 1.3,7.44 3.91,10.07 2.61,2.64 5.95,3.96 10.03,3.96 2.88,0 6,-0.99 9.36,-2.97l3.51,7.83 -2.97,1.62c-3.42,1.62 -7.2,2.43 -11.34,2.43 -6.48,0 -11.89,-2.17 -16.24,-6.52 -4.35,-4.35 -6.52,-9.79 -6.52,-16.33 0,-6.66 2.26,-12.22 6.79,-16.69 4.53,-4.47 10.18,-6.7 16.96,-6.7 4.56,0 8.91,1.02 13.04,3.06z"
android:strokeColor="#00000000" android:strokeWidth="4.80000019"/> android:fillAlpha="1"
<path android:fillAlpha="1" android:fillColor="#666666" android:strokeColor="#00000000"
android:pathData="m266.13,54 l-3.15,8.46c-1.8,-0.84 -3.42,-1.47 -4.86,-1.89 -1.44,-0.42 -2.91,-0.63 -4.41,-0.63 -3.96,0 -7.35,1.35 -10.17,4.05 -2.64,2.76 -3.96,6.18 -3.96,10.26 0,4.08 1.3,7.44 3.91,10.07 2.61,2.64 5.95,3.96 10.03,3.96 2.88,0 6,-0.99 9.36,-2.97l3.51,7.83 -2.97,1.62c-3.42,1.62 -7.2,2.43 -11.34,2.43 -6.48,0 -11.89,-2.17 -16.24,-6.52 -4.35,-4.35 -6.52,-9.79 -6.52,-16.33 0,-6.66 2.26,-12.22 6.79,-16.69 4.53,-4.47 10.18,-6.7 16.96,-6.7 4.56,0 8.91,1.02 13.04,3.06z" android:strokeColor="#00000000"/> android:fillColor="#666666"
<path android:fillAlpha="1" android:fillColor="#666666" android:strokeWidth="0.23809522"/>
android:pathData="m268.73,74.06c0,-6.6 2.26,-12.1 6.79,-16.51 4.53,-4.41 10.15,-6.61 16.87,-6.61 6.72,0 12.34,2.17 16.87,6.52 4.53,4.35 6.79,9.88 6.79,16.6 0,6.6 -2.25,12.1 -6.75,16.51 -4.5,4.41 -10.14,6.61 -16.91,6.61 -6.9,0 -12.56,-2.17 -17,-6.52 -4.44,-4.35 -6.66,-9.88 -6.66,-16.6zM292.39,59.58c-3.96,0 -7.27,1.38 -9.94,4.14 -2.67,2.76 -4,6.21 -4,10.35 0,2.16 0.3,4.05 0.9,5.67 0.6,1.62 1.62,3.18 3.06,4.68 2.64,2.7 5.97,4.05 9.99,4.05 3.96,0 7.27,-1.36 9.94,-4.09 2.67,-2.73 4,-6.16 4,-10.3 0,-2.16 -0.3,-4.05 -0.9,-5.67 -0.6,-1.62 -1.62,-3.18 -3.06,-4.68 -2.64,-2.76 -5.97,-4.14 -9.99,-4.14z" android:strokeColor="#00000000"/> <path
<path android:fillAlpha="1" android:fillColor="#666666" android:pathData="m268.73,74.15c0,-6.6 2.26,-12.1 6.79,-16.51 4.53,-4.41 10.15,-6.61 16.87,-6.61 6.72,0 12.34,2.17 16.87,6.52 4.53,4.35 6.79,9.88 6.79,16.6 0,6.6 -2.25,12.1 -6.75,16.51 -4.5,4.41 -10.14,6.61 -16.91,6.61 -6.9,0 -12.56,-2.17 -17,-6.52 -4.44,-4.35 -6.66,-9.88 -6.66,-16.6zM292.39,59.66c-3.96,0 -7.27,1.38 -9.94,4.14 -2.67,2.76 -4,6.21 -4,10.35 0,2.16 0.3,4.05 0.9,5.67 0.6,1.62 1.62,3.18 3.06,4.68 2.64,2.7 5.97,4.05 9.99,4.05 3.96,0 7.27,-1.36 9.94,-4.09 2.67,-2.73 4,-6.16 4,-10.3 0,-2.16 -0.3,-4.05 -0.9,-5.67 -0.6,-1.62 -1.62,-3.18 -3.06,-4.68 -2.64,-2.76 -5.97,-4.14 -9.99,-4.14z"
android:pathData="m321.56,96.85 l0,-43.72 9.99,0 0,43.72z" android:strokeColor="#00000000"/> android:fillAlpha="1"
<path android:fillAlpha="1" android:fillColor="#666666" android:strokeColor="#00000000"
android:pathData="m320.21,42.96c0,-1.62 0.6,-3.03 1.8,-4.23 1.32,-1.14 2.82,-1.71 4.5,-1.71 1.86,0 3.4,0.55 4.63,1.66 1.23,1.11 1.84,2.53 1.84,4.27 0,1.56 -0.63,2.97 -1.89,4.23 -1.32,1.14 -2.85,1.71 -4.59,1.71 -1.68,0 -3.15,-0.55 -4.41,-1.66 -1.26,-1.11 -1.89,-2.53 -1.89,-4.27z" android:strokeColor="#00000000"/> android:fillColor="#666666"
<path android:fillAlpha="1" android:fillColor="#666666" android:strokeWidth="0.23809522"/>
android:pathData="m338.84,96.85 l0,-43.72 9.99,0 0,6.75c3.24,-5.4 7.92,-8.1 14.03,-8.1 5.82,0 10.07,2.25 12.77,6.75 1.32,2.28 1.98,5.82 1.98,10.61l0,27.71 -9.99,0 0,-25.19 -0.27,-4.5c-0.24,-1.98 -1.2,-3.57 -2.88,-4.77 -1.56,-1.26 -3.39,-1.89 -5.49,-1.89 -3,0 -5.49,1.11 -7.47,3.33 -1.8,2.16 -2.7,5.22 -2.7,9.18l0,23.84z" android:strokeColor="#00000000"/> <path
<path android:fillAlpha="1" android:fillColor="#808080" android:pathData="m321.56,96.94v-43.72h9.99v43.72z"
android:pathData="m127.32,96.85 l0,-64.86 5.13,0 0,64.86z" android:strokeColor="#00000000"/> android:fillAlpha="1"
<path android:fillAlpha="1" android:fillColor="#808080" android:strokeColor="#00000000"
android:pathData="m142.99,96.85 l0,-43.72 5.22,0 0,43.72z" android:strokeColor="#00000000"/> android:fillColor="#666666"
<path android:fillAlpha="1" android:fillColor="#808080" android:strokeWidth="0.23809522"/>
android:pathData="m154.95,56.97 l0,-4.5 5.94,0 0,-12.24 5.22,0 0,12.24 12.14,0 0,4.5 -12.14,0 0,25.82c0,2.28 0.06,3.63 0.18,4.05 0.36,3.84 2.61,5.76 6.75,5.76 1.68,0 3.27,-0.33 4.77,-0.99l1.26,4.59c-2.64,0.66 -4.92,0.99 -6.84,0.99 -3.96,0 -7.02,-1.38 -9.18,-4.14 -0.84,-1.14 -1.44,-2.34 -1.8,-3.6 -0.12,-0.78 -0.21,-1.62 -0.27,-2.52 -0.06,-0.9 -0.09,-2.34 -0.09,-4.32l0,-25.64z" android:strokeColor="#00000000"/> <path
<path android:fillAlpha="1" android:fillColor="#808080" android:pathData="m320.21,43.05c0,-1.62 0.6,-3.03 1.8,-4.23 1.32,-1.14 2.82,-1.71 4.5,-1.71 1.86,0 3.4,0.55 4.63,1.66 1.23,1.11 1.84,2.53 1.84,4.27 0,1.56 -0.63,2.97 -1.89,4.23 -1.32,1.14 -2.85,1.71 -4.59,1.71 -1.68,0 -3.15,-0.55 -4.41,-1.66 -1.26,-1.11 -1.89,-2.53 -1.89,-4.27z"
android:pathData="m223.69,75.86 l-37.96,0c0,2.52 0.42,4.78 1.26,6.79 0.84,2.01 2.1,3.85 3.78,5.53 3.12,3 7.02,4.5 11.7,4.5 7.2,0 12.42,-3.51 15.65,-10.53l4.77,2.43c-4.14,8.4 -10.92,12.6 -20.33,12.6 -6.66,0 -12.04,-2.1 -16.15,-6.3 -4.11,-4.2 -6.16,-9.72 -6.16,-16.56 0,-6.84 2.08,-12.47 6.25,-16.91 4.17,-4.44 9.49,-6.66 15.97,-6.66 6.06,0 11.13,2.08 15.2,6.25 4.08,4.17 6.12,9.43 6.12,15.79l0,1.26zM202.37,55.26c-4.56,0 -8.26,1.38 -11.11,4.14 -2.85,2.76 -4.72,6.75 -5.62,11.96l32.48,0c0,-4.8 -1.48,-8.68 -4.45,-11.65 -2.97,-2.97 -6.73,-4.45 -11.29,-4.45z" android:strokeColor="#00000000"/> android:fillAlpha="1"
<path android:fillAlpha="1" android:fillColor="#808080" android:strokeColor="#00000000"
android:pathData="m139.47,42.96c0,-1.62 0.6,-3.03 1.8,-4.23 1.32,-1.14 2.82,-1.71 4.5,-1.71 1.86,0 3.4,0.55 4.63,1.66 1.23,1.11 1.84,2.53 1.84,4.27 0,1.56 -0.63,2.97 -1.89,4.23 -1.32,1.14 -2.85,1.71 -4.59,1.71 -1.68,0 -3.15,-0.55 -4.41,-1.66 -1.26,-1.11 -1.89,-2.53 -1.89,-4.27z" android:strokeColor="#00000000"/> android:fillColor="#666666"
android:strokeWidth="0.23809522"/>
<path
android:pathData="m338.84,96.94v-43.72h9.99v6.75c3.24,-5.4 7.92,-8.1 14.03,-8.1 5.82,0 10.07,2.25 12.77,6.75 1.32,2.28 1.98,5.82 1.98,10.61v27.71h-9.99v-25.19l-0.27,-4.5c-0.24,-1.98 -1.2,-3.57 -2.88,-4.77 -1.56,-1.26 -3.39,-1.89 -5.49,-1.89 -3,0 -5.49,1.11 -7.47,3.33 -1.8,2.16 -2.7,5.22 -2.7,9.18v23.84z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#666666"
android:strokeWidth="0.23809522"/>
<path
android:pathData="m127.32,96.94l0,-64.86l5.13,0l0,64.86z"
android:strokeColor="#00000000"
android:fillAlpha="1"
android:fillColor="#808080"/>
<path
android:pathData="m142.99,96.94l0,-43.72l5.22,0l0,43.72z"
android:strokeColor="#00000000"
android:fillAlpha="1"
android:fillColor="#808080"/>
<path
android:pathData="m154.95,57.06l0,-4.5l5.94,0l0,-12.24l5.22,0l0,12.24l12.14,0l0,4.5l-12.14,0l0,25.82c0,2.28 0.06,3.63 0.18,4.05 0.36,3.84 2.61,5.76 6.75,5.76 1.68,0 3.27,-0.33 4.77,-0.99l1.26,4.59c-2.64,0.66 -4.92,0.99 -6.84,0.99 -3.96,0 -7.02,-1.38 -9.18,-4.14 -0.84,-1.14 -1.44,-2.34 -1.8,-3.6 -0.12,-0.78 -0.21,-1.62 -0.27,-2.52 -0.06,-0.9 -0.09,-2.34 -0.09,-4.32l0,-25.64z"
android:strokeColor="#00000000"
android:fillAlpha="1"
android:fillColor="#808080"/>
<path
android:pathData="m223.69,75.95l-37.96,0c0,2.52 0.42,4.78 1.26,6.79 0.84,2.01 2.1,3.85 3.78,5.53 3.12,3 7.02,4.5 11.7,4.5 7.2,0 12.42,-3.51 15.65,-10.53l4.77,2.43c-4.14,8.4 -10.92,12.6 -20.33,12.6 -6.66,0 -12.04,-2.1 -16.15,-6.3 -4.11,-4.2 -6.16,-9.72 -6.16,-16.56 0,-6.84 2.08,-12.47 6.25,-16.91 4.17,-4.44 9.49,-6.66 15.97,-6.66 6.06,0 11.13,2.08 15.2,6.25 4.08,4.17 6.12,9.43 6.12,15.79l0,1.26zM202.37,55.35c-4.56,0 -8.26,1.38 -11.11,4.14 -2.85,2.76 -4.72,6.75 -5.62,11.96l32.48,0c0,-4.8 -1.48,-8.68 -4.45,-11.65 -2.97,-2.97 -6.73,-4.45 -11.29,-4.45z"
android:strokeColor="#00000000"
android:fillAlpha="1"
android:fillColor="#808080"/>
<path
android:pathData="m139.47,43.05c0,-1.62 0.6,-3.03 1.8,-4.23 1.32,-1.14 2.82,-1.71 4.5,-1.71 1.86,0 3.4,0.55 4.63,1.66 1.23,1.11 1.84,2.53 1.84,4.27 0,1.56 -0.63,2.97 -1.89,4.23 -1.32,1.14 -2.85,1.71 -4.59,1.71 -1.68,0 -3.15,-0.55 -4.41,-1.66 -1.26,-1.11 -1.89,-2.53 -1.89,-4.27z"
android:strokeColor="#00000000"
android:fillAlpha="1"
android:fillColor="#808080"/>
</vector> </vector>

View File

@ -0,0 +1,38 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="90dp"
android:height="90dp"
android:viewportWidth="90.0"
android:viewportHeight="90.0">
<path
android:pathData="m45,9.73c19.49,0 35.27,15.78 35.27,35.27 0,19.49 -15.78,35.27 -35.27,35.27 -19.49,0 -35.27,-15.78 -35.27,-35.27 0,-19.49 15.78,-35.27 35.27,-35.27z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="m45,7.73c-20.57,0 -37.27,16.7 -37.27,37.27 0,20.57 16.7,37.27 37.27,37.27 20.57,0 37.27,-16.7 37.27,-37.27 0,-20.57 -16.7,-37.27 -37.27,-37.27zM45,9.73c19.49,0 35.27,15.78 35.27,35.27 0,19.49 -15.78,35.27 -35.27,35.27 -19.49,0 -35.27,-15.78 -35.27,-35.27 0,-19.49 15.78,-35.27 35.27,-35.27z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#0066cc"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="M30.78,56.85h27.97v4.04h-27.97z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#0066cc"
android:strokeWidth="2"
android:strokeAlpha="1"/>
<path
android:pathData="m38.8,26.93 l0,12.18 -7.71,0 13.86,13.57 13.86,-13.57 -7.83,0 0,-12.18z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#0066cc"
android:strokeWidth="2"
android:strokeAlpha="1"/>
</vector>

View File

@ -0,0 +1,33 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="90dp"
android:height="90dp"
android:viewportWidth="90.0"
android:viewportHeight="90.0">
<path
android:pathData="m45,9.73c19.49,0 35.27,15.78 35.27,35.27 0,19.49 -15.78,35.27 -35.27,35.27 -19.49,0 -35.27,-15.78 -35.27,-35.27 0,-19.49 15.78,-35.27 35.27,-35.27z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="m45,7.73c-2.14,0 -4.24,0.19 -6.29,0.54l0.44,1.95c1.9,-0.32 3.86,-0.49 5.85,-0.49 19.49,0 35.27,15.78 35.27,35.27 0,19.49 -15.78,35.27 -35.27,35.27 -19.49,0 -35.27,-15.78 -35.27,-35.27 0,-1.94 0.16,-3.85 0.46,-5.7l-1.97,-0.35c-0.32,1.97 -0.5,3.99 -0.5,6.05 0,20.57 16.7,37.27 37.27,37.27 20.57,0 37.27,-16.7 37.27,-37.27 0,-20.57 -16.7,-37.27 -37.27,-37.27zM30.57,10.64c-3,1.26 -5.8,2.92 -8.34,4.88l1.26,1.53c2.41,-1.86 5.07,-3.42 7.92,-4.61l-0.85,-1.81zM16.08,21.52c-2.24,2.75 -4.09,5.82 -5.47,9.14l1.84,0.76c1.31,-3.15 3.07,-6.06 5.19,-8.67l-1.56,-1.23z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#0066cc"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="m59.21,32.08a2.5,2.5 0,0 0,-1.72 0.76l-18.12,18.12 -8.09,-8.09a2.5,2.5 0,1 0,-3.54 3.54l9.85,9.86a2.5,2.5 0,0 0,3.54 0l19.89,-19.89a2.5,2.5 0,0 0,-1.82 -4.29z"
android:strokeLineCap="round"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#0066cc"
android:strokeWidth="5"
android:strokeLineJoin="round"
android:strokeAlpha="1"/>
</vector>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<level-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_download_progress_0" android:minLevel="0" android:maxLevel="14" />
<item android:drawable="@drawable/ic_download_progress_15" android:minLevel="15" android:maxLevel="29" />
<item android:drawable="@drawable/ic_download_progress_30" android:minLevel="30" android:maxLevel="44" />
<item android:drawable="@drawable/ic_download_progress_45" android:minLevel="45" android:maxLevel="59" />
<item android:drawable="@drawable/ic_download_progress_60" android:minLevel="60" android:maxLevel="74" />
<item android:drawable="@drawable/ic_download_progress_75" android:minLevel="75" android:maxLevel="89" />
<item android:drawable="@drawable/ic_download_progress_90" android:minLevel="90" android:maxLevel="104" />
<item android:drawable="@drawable/ic_download_progress_105" android:minLevel="105" android:maxLevel="119" />
<item android:drawable="@drawable/ic_download_progress_120" android:minLevel="120" android:maxLevel="134" />
<item android:drawable="@drawable/ic_download_progress_135" android:minLevel="135" android:maxLevel="149" />
<item android:drawable="@drawable/ic_download_progress_150" android:minLevel="150" android:maxLevel="164" />
<item android:drawable="@drawable/ic_download_progress_165" android:minLevel="165" android:maxLevel="179" />
<item android:drawable="@drawable/ic_download_progress_180" android:minLevel="180" android:maxLevel="194" />
<item android:drawable="@drawable/ic_download_progress_195" android:minLevel="195" android:maxLevel="209" />
<item android:drawable="@drawable/ic_download_progress_210" android:minLevel="210" android:maxLevel="224" />
<item android:drawable="@drawable/ic_download_progress_225" android:minLevel="225" android:maxLevel="239" />
<item android:drawable="@drawable/ic_download_progress_240" android:minLevel="240" android:maxLevel="254" />
<item android:drawable="@drawable/ic_download_progress_255" android:minLevel="255" android:maxLevel="269" />
<item android:drawable="@drawable/ic_download_progress_270" android:minLevel="270" android:maxLevel="284" />
<item android:drawable="@drawable/ic_download_progress_285" android:minLevel="285" android:maxLevel="299" />
<item android:drawable="@drawable/ic_download_progress_300" android:minLevel="300" android:maxLevel="314" />
<item android:drawable="@drawable/ic_download_progress_315" android:minLevel="315" android:maxLevel="329" />
<item android:drawable="@drawable/ic_download_progress_330" android:minLevel="330" android:maxLevel="344" />
<item android:drawable="@drawable/ic_download_progress_345" android:minLevel="345" android:maxLevel="359" />
<item android:drawable="@drawable/ic_download_progress_360" android:minLevel="360" />
</level-list>

View File

@ -0,0 +1,29 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="90dp"
android:height="90dp"
android:viewportWidth="90.0"
android:viewportHeight="90.0">
<path
android:pathData="m45,7.73c-20.57,0 -37.27,16.7 -37.27,37.27 0,20.57 16.7,37.27 37.27,37.27 20.57,0 37.27,-16.7 37.27,-37.27 0,-20.57 -16.7,-37.27 -37.27,-37.27z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#0066cc"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="M30.78,56.85h27.97v4.04h-27.97z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
<path
android:pathData="m38.8,26.93 l0,12.18 -7.71,0 13.86,13.57 13.86,-13.57 -7.83,0 0,-12.18z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
</vector>

View File

@ -0,0 +1,38 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="90dp"
android:height="90dp"
android:viewportWidth="90.0"
android:viewportHeight="90.0">
<path
android:pathData="M45,0.39C44.97,0.39 44.94,0.39 44.9,0.39L45,44.98L45,44.99L45,45L45,45L45,45L45,45L45.02,45L46.51,45L45.02,45L45,45L88.08,56.56C89.07,52.88 89.61,49 89.61,45L89.61,45C89.61,44.97 89.61,44.94 89.61,44.9L89.6,44.9C89.59,41.12 89.11,37.28 88.09,33.46C88.08,33.42 88.07,33.39 88.06,33.36L88.06,33.36C87.07,29.72 85.62,26.14 83.64,22.71C83.64,22.71 83.63,22.7 83.63,22.7C83.63,22.7 83.63,22.69 83.63,22.69C81.72,19.4 79.37,16.29 76.55,13.47C76.55,13.47 76.55,13.46 76.54,13.46L76.54,13.46C76.52,13.43 76.49,13.41 76.47,13.39C73.79,10.72 70.73,8.35 67.3,6.37C67.28,6.35 67.25,6.34 67.22,6.32L67.22,6.33C63.94,4.45 60.38,2.94 56.56,1.92C56.56,1.92 56.56,1.92 56.56,1.92C56.56,1.92 56.55,1.91 56.54,1.91L56.54,1.91C56.51,1.9 56.48,1.9 56.45,1.89L56.45,1.89C52.8,0.92 48.96,0.39 45,0.39L45,0.39zM45.41,44.29L45.01,44.98L45,44.99L45.41,44.29z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#8ab000"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="m45,7.73c-20.57,0 -37.27,16.7 -37.27,37.27 0,20.57 16.7,37.27 37.27,37.27 20.57,0 37.27,-16.7 37.27,-37.27 0,-20.57 -16.7,-37.27 -37.27,-37.27z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#0066cc"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="M30.78,56.85h27.97v4.04h-27.97z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
<path
android:pathData="m38.8,26.93 l0,12.18 -7.71,0 13.86,13.57 13.86,-13.57 -7.83,0 0,-12.18z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
</vector>

View File

@ -0,0 +1,38 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="90dp"
android:height="90dp"
android:viewportWidth="90.0"
android:viewportHeight="90.0">
<path
android:pathData="M45,0.39C44.97,0.39 44.94,0.39 44.9,0.39L45,44.98L45,44.99L45,45L45,45L45.01,45L83.62,67.32C85.53,64.02 87.05,60.42 88.08,56.56L88.08,56.56C88.09,56.56 88.09,56.55 88.09,56.54C88.09,56.54 88.09,56.54 88.09,56.54C88.09,56.52 88.1,56.51 88.1,56.49C89.08,52.82 89.61,48.97 89.61,45L89.61,45C89.61,44.97 89.61,44.94 89.61,44.9L89.6,44.9C89.59,41.12 89.11,37.28 88.09,33.46C88.08,33.42 88.07,33.39 88.06,33.36L88.06,33.36C87.07,29.72 85.62,26.14 83.64,22.71C83.64,22.71 83.63,22.7 83.63,22.7C83.63,22.7 83.63,22.69 83.63,22.69C81.72,19.4 79.37,16.29 76.55,13.47C76.55,13.47 76.55,13.46 76.54,13.46L76.54,13.46C76.52,13.43 76.49,13.41 76.47,13.39C73.79,10.72 70.73,8.35 67.3,6.37C67.28,6.35 67.25,6.34 67.22,6.32L67.22,6.33C63.94,4.45 60.38,2.94 56.56,1.92C56.56,1.92 56.56,1.92 56.56,1.92C56.56,1.92 56.55,1.91 56.54,1.91L56.54,1.91C56.51,1.9 56.48,1.9 56.45,1.89L56.45,1.89C52.8,0.92 48.96,0.39 45,0.39L45,0.39zM45.41,44.29L45.01,44.98L45,44.99L45.41,44.29zM45.02,45L46.51,45L45.02,45L45.01,45L45.02,45z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#8ab000"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="m45,7.73c-20.57,0 -37.27,16.7 -37.27,37.27 0,20.57 16.7,37.27 37.27,37.27 20.57,0 37.27,-16.7 37.27,-37.27 0,-20.57 -16.7,-37.27 -37.27,-37.27z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#0066cc"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="M30.78,56.85h27.97v4.04h-27.97z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
<path
android:pathData="m38.8,26.93 l0,12.18 -7.71,0 13.86,13.57 13.86,-13.57 -7.83,0 0,-12.18z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
</vector>

View File

@ -0,0 +1,38 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="90dp"
android:height="90dp"
android:viewportWidth="90.0"
android:viewportHeight="90.0">
<path
android:pathData="M45,0.39C44.97,0.39 44.94,0.39 44.9,0.39L45,44.98L45,44.99L45,45L45,45L45.01,45L83.62,67.32C85.53,64.02 87.05,60.42 88.08,56.56L88.08,56.56C88.09,56.56 88.09,56.55 88.09,56.54C88.09,56.54 88.09,56.54 88.09,56.54C88.09,56.52 88.1,56.51 88.1,56.49C89.08,52.82 89.61,48.97 89.61,45L89.61,45C89.61,44.97 89.61,44.94 89.61,44.9L89.6,44.9C89.59,41.12 89.11,37.28 88.09,33.46C88.08,33.42 88.07,33.39 88.06,33.36L88.06,33.36C87.07,29.72 85.62,26.14 83.64,22.71C83.64,22.71 83.63,22.7 83.63,22.7C83.63,22.7 83.63,22.69 83.63,22.69C81.72,19.4 79.37,16.29 76.55,13.47C76.55,13.47 76.55,13.46 76.54,13.46L76.54,13.46C76.52,13.43 76.49,13.41 76.47,13.39C73.79,10.72 70.73,8.35 67.3,6.37C67.28,6.35 67.25,6.34 67.22,6.32L67.22,6.33C63.94,4.45 60.38,2.94 56.56,1.92C56.56,1.92 56.56,1.92 56.56,1.92C56.56,1.92 56.55,1.91 56.54,1.91L56.54,1.91C56.51,1.9 56.48,1.9 56.45,1.89L56.45,1.89C52.8,0.92 48.96,0.39 45,0.39L45,0.39zM45.41,44.29L45.01,44.98L45,44.99L45.41,44.29zM45.02,45L46.51,45L45.02,45L45.01,45L45.02,45z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#8ab000"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="m45,7.73c-20.57,0 -37.27,16.7 -37.27,37.27 0,20.57 16.7,37.27 37.27,37.27 20.57,0 37.27,-16.7 37.27,-37.27C82.27,24.43 65.57,7.73 45,7.73Z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#0066cc"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="M30.78,56.85h27.97v4.04h-27.97z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
<path
android:pathData="m38.8,26.93 l0,12.18 -7.71,0 13.86,13.57 13.86,-13.57 -7.83,0 0,-12.18z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
</vector>

View File

@ -0,0 +1,38 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="90dp"
android:height="90dp"
android:viewportWidth="90.0"
android:viewportHeight="90.0">
<path
android:pathData="m45,0.39c-0.03,0 -0.06,0 -0.1,0l0.1,44.59 0,0.02 11.56,-43.09c-3.69,-0.99 -7.56,-1.53 -11.56,-1.53z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#8ab000"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="m45,7.73c-20.57,0 -37.27,16.7 -37.27,37.27 0,20.57 16.7,37.27 37.27,37.27 20.57,0 37.27,-16.7 37.27,-37.27 0,-20.57 -16.7,-37.27 -37.27,-37.27z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#0066cc"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="M30.78,56.85h27.97v4.04h-27.97z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
<path
android:pathData="m38.8,26.93 l0,12.18 -7.71,0 13.86,13.57 13.86,-13.57 -7.83,0 0,-12.18z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
</vector>

View File

@ -0,0 +1,38 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="90dp"
android:height="90dp"
android:viewportWidth="90.0"
android:viewportHeight="90.0">
<path
android:pathData="M45,0.39C44.97,0.39 44.94,0.39 44.9,0.39L45,44.98L45,44.99L45,45L45,45L45,45L45,45L45.01,45L46.26,45.72L45.02,45.01L45,45L76.53,76.56C79.23,73.86 81.63,70.77 83.63,67.3C83.65,67.28 83.66,67.25 83.68,67.22L83.67,67.22C85.55,63.94 87.06,60.38 88.08,56.56L88.08,56.56C88.09,56.56 88.09,56.55 88.09,56.54C88.09,56.54 88.09,56.54 88.09,56.54C88.09,56.52 88.1,56.51 88.1,56.49C89.08,52.82 89.61,48.97 89.61,45L89.61,45C89.61,44.97 89.61,44.94 89.61,44.9L89.6,44.9C89.59,41.12 89.11,37.28 88.09,33.46C88.08,33.42 88.07,33.39 88.06,33.36L88.06,33.36C87.07,29.72 85.62,26.14 83.64,22.71C83.64,22.71 83.63,22.7 83.63,22.7C83.63,22.7 83.63,22.69 83.63,22.69C81.72,19.4 79.37,16.29 76.55,13.47C76.55,13.47 76.55,13.46 76.54,13.46L76.54,13.46C76.52,13.43 76.49,13.41 76.47,13.39C73.79,10.72 70.73,8.35 67.3,6.37C67.28,6.35 67.25,6.34 67.22,6.32L67.22,6.33C63.94,4.45 60.38,2.94 56.56,1.92C56.56,1.92 56.56,1.92 56.56,1.92C56.56,1.92 56.55,1.91 56.54,1.91L56.54,1.91C56.51,1.9 56.48,1.9 56.45,1.89L56.45,1.89C52.8,0.92 48.96,0.39 45,0.39L45,0.39zM45.41,44.29L45.01,44.98L45,44.99L45.41,44.29zM45.02,45L46.51,45L45.02,45L45.01,45L45.02,45z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#8ab000"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="m45,7.73c-20.57,0 -37.27,16.7 -37.27,37.27 0,20.57 16.7,37.27 37.27,37.27 20.57,0 37.27,-16.7 37.27,-37.27C82.27,24.43 65.57,7.73 45,7.73Z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#0066cc"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="M30.78,56.85h27.97v4.04h-27.97z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
<path
android:pathData="m38.8,26.93 l0,12.18 -7.71,0 13.86,13.57 13.86,-13.57 -7.83,0 0,-12.18z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
</vector>

View File

@ -0,0 +1,38 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="90dp"
android:height="90dp"
android:viewportWidth="90.0"
android:viewportHeight="90.0">
<path
android:pathData="M45,0.39C44.97,0.39 44.94,0.39 44.9,0.39L45,44.98L45,44.99L45,45L45,45L45,45L67.29,83.64C70.59,81.73 73.7,79.38 76.53,76.55L76.53,76.56C76.53,76.55 76.54,76.55 76.54,76.54C79.24,73.85 81.63,70.76 83.63,67.3C83.65,67.28 83.66,67.25 83.68,67.22L83.67,67.22C85.55,63.94 87.06,60.38 88.08,56.56L88.08,56.56C88.09,56.56 88.09,56.55 88.09,56.54C88.09,56.54 88.09,56.54 88.09,56.54C88.09,56.52 88.1,56.51 88.1,56.49C89.08,52.82 89.61,48.97 89.61,45L89.61,45C89.61,44.97 89.61,44.94 89.61,44.9L89.6,44.9C89.59,41.12 89.11,37.28 88.09,33.46C88.08,33.42 88.07,33.39 88.06,33.36L88.06,33.36C87.07,29.72 85.62,26.14 83.64,22.71C83.64,22.71 83.63,22.7 83.63,22.7C83.63,22.7 83.63,22.69 83.63,22.69C81.72,19.4 79.37,16.29 76.55,13.47C76.55,13.47 76.55,13.46 76.54,13.46L76.54,13.46C76.52,13.43 76.49,13.41 76.47,13.39C73.79,10.72 70.73,8.35 67.3,6.37C67.28,6.35 67.25,6.34 67.22,6.32L67.22,6.33C63.94,4.45 60.38,2.94 56.56,1.92C56.56,1.92 56.56,1.92 56.56,1.92C56.56,1.92 56.55,1.91 56.54,1.91L56.54,1.91C56.51,1.9 56.48,1.9 56.45,1.89L56.45,1.89C52.8,0.92 48.96,0.39 45,0.39L45,0.39zM45.41,44.29L45.01,44.98L45,44.99L45.41,44.29zM45.02,45L46.51,45L45.02,45L45.01,45L45.02,45zM45,45L45.01,45L46.26,45.72L45.02,45.01L45,45z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#8ab000"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="m45,7.73c-20.57,0 -37.27,16.7 -37.27,37.27 0,20.57 16.7,37.27 37.27,37.27 20.57,0 37.27,-16.7 37.27,-37.27C82.27,24.43 65.57,7.73 45,7.73Z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#0066cc"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="M30.78,56.85h27.97v4.04h-27.97z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
<path
android:pathData="m38.8,26.93 l0,12.18 -7.71,0 13.86,13.57 13.86,-13.57 -7.83,0 0,-12.18z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
</vector>

View File

@ -0,0 +1,38 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="90dp"
android:height="90dp"
android:viewportWidth="90.0"
android:viewportHeight="90.0">
<path
android:pathData="M45,0.39C44.97,0.39 44.94,0.39 44.9,0.39L45,44.98L45,44.99L45,45L45,45L45,45L45,45.02L44.99,89.61C48.8,89.61 52.68,89.12 56.54,88.09C56.58,88.08 56.61,88.07 56.64,88.06L56.64,88.06C60.28,87.07 63.86,85.62 67.29,83.64C67.29,83.64 67.3,83.63 67.3,83.63C67.3,83.63 67.31,83.63 67.31,83.63C70.6,81.72 73.71,79.37 76.53,76.55L76.53,76.56C76.53,76.55 76.54,76.55 76.54,76.54C79.24,73.85 81.63,70.76 83.63,67.3C83.65,67.28 83.66,67.25 83.68,67.22L83.67,67.22C85.55,63.94 87.06,60.38 88.08,56.56L88.08,56.56C88.09,56.56 88.09,56.55 88.09,56.54C88.09,56.54 88.09,56.54 88.09,56.54C88.09,56.52 88.1,56.51 88.1,56.49C89.08,52.82 89.61,48.97 89.61,45L89.61,45C89.61,44.97 89.61,44.94 89.61,44.9L89.6,44.9C89.59,41.12 89.11,37.28 88.09,33.46C88.08,33.42 88.07,33.39 88.06,33.36L88.06,33.36C87.07,29.72 85.62,26.14 83.64,22.71C83.64,22.71 83.63,22.7 83.63,22.7C83.63,22.7 83.63,22.69 83.63,22.69C81.72,19.4 79.37,16.29 76.55,13.47C76.55,13.47 76.55,13.46 76.54,13.46L76.54,13.46C76.52,13.43 76.49,13.41 76.47,13.39C73.79,10.72 70.73,8.35 67.3,6.37C67.28,6.35 67.25,6.34 67.22,6.32L67.22,6.33C63.94,4.45 60.38,2.94 56.56,1.92C56.56,1.92 56.56,1.92 56.56,1.92C56.56,1.92 56.55,1.91 56.54,1.91L56.54,1.91C56.51,1.9 56.48,1.9 56.45,1.89L56.45,1.89C52.8,0.92 48.96,0.39 45,0.39L45,0.39zM45.41,44.29L45.01,44.98L45,44.99L45.41,44.29zM45.02,45L46.51,45L45.02,45L45.01,45L45.02,45zM45,45L45.01,45L45.01,45L46.26,45.72L45.02,45.01L45,45z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#8ab000"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="m45,7.73c-20.57,0 -37.27,16.7 -37.27,37.27 0,20.57 16.7,37.27 37.27,37.27 20.57,0 37.27,-16.7 37.27,-37.27 0,-20.57 -16.7,-37.27 -37.27,-37.27z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#0066cc"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="M30.78,56.85h27.97v4.04h-27.97z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
<path
android:pathData="m38.8,26.93 l0,12.18 -7.71,0 13.86,13.57 13.86,-13.57 -7.83,0 0,-12.18z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
</vector>

View File

@ -0,0 +1,38 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="90dp"
android:height="90dp"
android:viewportWidth="90.0"
android:viewportHeight="90.0">
<path
android:pathData="M45,0.39C44.97,0.39 44.94,0.39 44.9,0.39L45,44.98L45,44.99L45,44.99L44.99,45L45,45L45,45L45,45L45,45.02L45,47.25L45,45.02L45,45L33.43,88.08C37.12,89.07 41,89.61 45,89.61L45,89.61C45.03,89.61 45.07,89.61 45.1,89.61L45.1,89.6C48.88,89.59 52.72,89.11 56.54,88.09C56.58,88.08 56.61,88.07 56.64,88.06L56.64,88.06C60.28,87.07 63.86,85.62 67.29,83.64C67.29,83.64 67.3,83.63 67.3,83.63C67.3,83.63 67.31,83.63 67.31,83.63C70.6,81.72 73.71,79.37 76.53,76.55L76.53,76.56C76.53,76.55 76.54,76.55 76.54,76.54C79.24,73.85 81.63,70.76 83.63,67.3C83.65,67.28 83.66,67.25 83.68,67.22L83.67,67.22C85.55,63.94 87.06,60.38 88.08,56.56L88.08,56.56C88.09,56.56 88.09,56.55 88.09,56.54C88.09,56.54 88.09,56.54 88.09,56.54C88.09,56.52 88.1,56.51 88.1,56.49C89.08,52.82 89.61,48.97 89.61,45L89.61,45C89.61,44.97 89.61,44.94 89.61,44.9L89.6,44.9C89.59,41.12 89.11,37.28 88.09,33.46C88.08,33.42 88.07,33.39 88.06,33.36L88.06,33.36C87.07,29.72 85.62,26.14 83.64,22.71C83.64,22.71 83.63,22.7 83.63,22.7C83.63,22.7 83.63,22.69 83.63,22.69C81.72,19.4 79.37,16.29 76.55,13.47C76.55,13.47 76.55,13.46 76.54,13.46L76.54,13.46C76.52,13.43 76.49,13.41 76.47,13.39C73.79,10.72 70.73,8.35 67.3,6.37C67.28,6.35 67.25,6.34 67.22,6.32L67.22,6.33C63.94,4.45 60.38,2.94 56.56,1.92C56.56,1.92 56.56,1.92 56.56,1.92C56.56,1.92 56.55,1.91 56.54,1.91L56.54,1.91C56.51,1.9 56.48,1.9 56.45,1.89L56.45,1.89C52.8,0.92 48.96,0.39 45,0.39L45,0.39zM45.41,44.29L45.01,44.98L45,44.99L45.01,44.98L45.41,44.29zM45.02,45L46.51,45L45.02,45L45.01,45L45.02,45zM45,45L45.01,45L46.26,45.72L45.02,45.01L45,45z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#8ab000"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="m45,7.73c-20.57,0 -37.27,16.7 -37.27,37.27 0,20.57 16.7,37.27 37.27,37.27 20.57,0 37.27,-16.7 37.27,-37.27 0,-20.57 -16.7,-37.27 -37.27,-37.27z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#0066cc"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="M30.78,56.85h27.97v4.04h-27.97z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
<path
android:pathData="m38.8,26.93 l0,12.18 -7.71,0 13.86,13.57 13.86,-13.57 -7.83,0 0,-12.18z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
</vector>

View File

@ -0,0 +1,38 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="90dp"
android:height="90dp"
android:viewportWidth="90.0"
android:viewportHeight="90.0">
<path
android:pathData="M45,0.39C44.97,0.39 44.94,0.39 44.9,0.39L45,44.98L45,44.99L45,44.99L44.99,45L45,45L45,45L45,45L45,45.02L45,47.25L45,45.02L45,45L44.99,45.03L22.68,83.62C25.98,85.53 29.58,87.05 33.43,88.08L33.43,88.08C37.12,89.07 41,89.61 45,89.61L45,89.61C45.03,89.61 45.07,89.61 45.1,89.61L45.1,89.6C48.88,89.59 52.72,89.11 56.54,88.09C56.58,88.08 56.61,88.07 56.64,88.06L56.64,88.06C60.28,87.07 63.86,85.62 67.29,83.64C67.29,83.64 67.3,83.63 67.3,83.63C67.3,83.63 67.31,83.63 67.31,83.63C70.6,81.72 73.71,79.37 76.53,76.55L76.53,76.56C76.53,76.55 76.54,76.55 76.54,76.54C79.24,73.85 81.63,70.76 83.63,67.3C83.65,67.28 83.66,67.25 83.68,67.22L83.67,67.22C85.55,63.94 87.06,60.38 88.08,56.56L88.08,56.56C88.09,56.56 88.09,56.55 88.09,56.54C88.09,56.54 88.09,56.54 88.09,56.54C88.09,56.52 88.1,56.51 88.1,56.49C89.08,52.82 89.61,48.97 89.61,45L89.61,45C89.61,44.97 89.61,44.94 89.61,44.9L89.6,44.9C89.59,41.12 89.11,37.28 88.09,33.46C88.08,33.42 88.07,33.39 88.06,33.36L88.06,33.36C87.07,29.72 85.62,26.14 83.64,22.71C83.64,22.71 83.63,22.7 83.63,22.7C83.63,22.7 83.63,22.69 83.63,22.69C81.72,19.4 79.37,16.29 76.55,13.47C76.55,13.47 76.55,13.46 76.54,13.46L76.54,13.46C76.52,13.43 76.49,13.41 76.47,13.39C73.79,10.72 70.73,8.35 67.3,6.37C67.28,6.35 67.25,6.34 67.22,6.32L67.22,6.33C63.94,4.45 60.38,2.94 56.56,1.92C56.56,1.92 56.56,1.92 56.56,1.92C56.56,1.92 56.55,1.91 56.54,1.91L56.54,1.91C56.51,1.9 56.48,1.9 56.45,1.89L56.45,1.89C52.8,0.92 48.96,0.39 45,0.39L45,0.39zM45.41,44.29L45.01,44.98L45,44.99L45.01,44.98L45.41,44.29zM45.02,45L46.51,45L45.02,45L45.01,45L45.02,45zM45,45L45.01,45L46.26,45.72L45.02,45.01L45,45z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#8ab000"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="m45,7.73c-20.57,0 -37.27,16.7 -37.27,37.27 0,20.57 16.7,37.27 37.27,37.27 20.57,0 37.27,-16.7 37.27,-37.27 0,-20.57 -16.7,-37.27 -37.27,-37.27z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#0066cc"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="M30.78,56.85h27.97v4.04h-27.97z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
<path
android:pathData="m38.8,26.93 l0,12.18 -7.71,0 13.86,13.57 13.86,-13.57 -7.83,0 0,-12.18z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
</vector>

View File

@ -0,0 +1,38 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="90dp"
android:height="90dp"
android:viewportWidth="90.0"
android:viewportHeight="90.0">
<path
android:pathData="M45,0.39C44.97,0.39 44.94,0.39 44.9,0.39L45,44.98L45,44.99L45,45L45,45L45,45L45,45.02L45,47.25L45,45.02L45,45L45,45L44.99,45L13.44,76.53C16.14,79.23 19.23,81.63 22.7,83.63C22.72,83.65 22.75,83.66 22.78,83.68L22.78,83.67C26.06,85.55 29.62,87.06 33.43,88.08L33.43,88.08C37.12,89.07 41,89.61 45,89.61L45,89.61C45.03,89.61 45.07,89.61 45.1,89.61L45.1,89.6C48.88,89.59 52.72,89.11 56.54,88.09C56.58,88.08 56.61,88.07 56.64,88.06L56.64,88.06C60.28,87.07 63.86,85.62 67.29,83.64C67.29,83.64 67.3,83.63 67.3,83.63C67.3,83.63 67.31,83.63 67.31,83.63C70.6,81.72 73.71,79.37 76.53,76.55L76.53,76.56C76.53,76.55 76.54,76.55 76.54,76.54C79.24,73.85 81.63,70.76 83.63,67.3C83.65,67.28 83.66,67.25 83.68,67.22L83.67,67.22C85.55,63.94 87.06,60.38 88.08,56.56L88.08,56.56C88.09,56.56 88.09,56.55 88.09,56.54C88.09,56.54 88.09,56.54 88.09,56.54C88.09,56.52 88.1,56.51 88.1,56.49C89.08,52.82 89.61,48.97 89.61,45L89.61,45C89.61,44.97 89.61,44.94 89.61,44.9L89.6,44.9C89.59,41.12 89.11,37.28 88.09,33.46C88.08,33.42 88.07,33.39 88.06,33.36L88.06,33.36C87.07,29.72 85.62,26.14 83.64,22.71C83.64,22.71 83.63,22.7 83.63,22.7C83.63,22.7 83.63,22.69 83.63,22.69C81.72,19.4 79.37,16.29 76.55,13.47C76.55,13.47 76.55,13.46 76.54,13.46L76.54,13.46C76.52,13.43 76.49,13.41 76.47,13.39C73.79,10.72 70.73,8.35 67.3,6.37C67.28,6.35 67.25,6.34 67.22,6.32L67.22,6.33C63.94,4.45 60.38,2.94 56.56,1.92C56.56,1.92 56.56,1.92 56.56,1.92C56.56,1.92 56.55,1.91 56.54,1.91L56.54,1.91C56.51,1.9 56.48,1.9 56.45,1.89L56.45,1.89C52.8,0.92 48.96,0.39 45,0.39L45,0.39zM45.41,44.29L45.01,44.98L45,44.99L45.41,44.29zM45.02,45L46.51,45L45.02,45L45.01,45L45.02,45zM45,45L45.01,45L45.01,45L46.26,45.72L45.02,45.01L45,45zM45,45.01L44.99,45.03L44.04,46.68L44.99,45.02L45,45.01z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#8ab000"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="m45,7.73c-20.57,0 -37.27,16.7 -37.27,37.27 0,20.57 16.7,37.27 37.27,37.27 20.57,0 37.27,-16.7 37.27,-37.27 0,-20.57 -16.7,-37.27 -37.27,-37.27z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#0066cc"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="M30.78,56.85h27.97v4.04h-27.97z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
<path
android:pathData="m38.8,26.93 l0,12.18 -7.71,0 13.86,13.57 13.86,-13.57 -7.83,0 0,-12.18z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
</vector>

View File

@ -0,0 +1,17 @@
<vector android:height="24dp" android:viewportHeight="90.0"
android:viewportWidth="90.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillAlpha="1" android:fillColor="#8ab000"
android:pathData="M45,0.39C44.97,0.39 44.94,0.39 44.9,0.39L45,44.98L45,44.99L45,45L45,45L44.99,45L44.99,45L6.36,67.29C8.27,70.59 10.62,73.7 13.44,76.53C13.45,76.53 13.45,76.53 13.45,76.54C13.45,76.54 13.45,76.54 13.46,76.54C16.15,79.24 19.24,81.63 22.7,83.63C22.72,83.65 22.75,83.66 22.78,83.68L22.78,83.67C26.06,85.55 29.62,87.06 33.43,88.08L33.43,88.08C37.12,89.07 41,89.61 45,89.61L45,89.61C45.03,89.61 45.07,89.61 45.1,89.61L45.1,89.6C48.88,89.59 52.72,89.11 56.54,88.09C56.58,88.08 56.61,88.07 56.64,88.06L56.64,88.06C60.28,87.07 63.86,85.62 67.29,83.64C67.29,83.64 67.3,83.63 67.3,83.63C67.3,83.63 67.31,83.63 67.31,83.63C70.6,81.72 73.71,79.37 76.53,76.55L76.53,76.56C76.53,76.55 76.54,76.55 76.54,76.54C79.24,73.85 81.63,70.76 83.63,67.3C83.65,67.28 83.66,67.25 83.68,67.22L83.67,67.22C85.55,63.94 87.06,60.38 88.08,56.56L88.08,56.56C88.09,56.56 88.09,56.55 88.09,56.54C88.09,56.54 88.09,56.54 88.09,56.54C88.09,56.52 88.1,56.51 88.1,56.49C89.08,52.82 89.61,48.97 89.61,45L89.61,45C89.61,44.97 89.61,44.94 89.61,44.9L89.6,44.9C89.59,41.12 89.11,37.28 88.09,33.46C88.08,33.42 88.07,33.39 88.06,33.36L88.06,33.36C87.07,29.72 85.62,26.14 83.64,22.71C83.64,22.71 83.63,22.7 83.63,22.7C83.63,22.7 83.63,22.69 83.63,22.69C81.72,19.4 79.37,16.29 76.55,13.47C76.55,13.47 76.55,13.46 76.54,13.46L76.54,13.46C76.52,13.43 76.49,13.41 76.47,13.39C73.79,10.72 70.73,8.35 67.3,6.37C67.28,6.35 67.25,6.34 67.22,6.32L67.22,6.33C63.94,4.45 60.38,2.94 56.56,1.92C56.56,1.92 56.56,1.92 56.56,1.92C56.56,1.92 56.55,1.91 56.54,1.91L56.54,1.91C56.51,1.9 56.48,1.9 56.45,1.89L56.45,1.89C52.8,0.92 48.96,0.39 45,0.39L45,0.39zM45.41,44.29L45.01,44.98L45,44.99L45.41,44.29zM45.02,45L46.51,45L45.02,45L45.01,45L45.02,45zM45,45L45.01,45L45.01,45L46.26,45.72L45.02,45.01L45,45zM45,45L45,45L45,45.02L45,47.25L45,45.02L45,45zM45,45.01L44.99,45.03L44.04,46.68L44.99,45.02L45,45.01z"
android:strokeAlpha="1" android:strokeColor="#00000000"
android:strokeLineCap="butt" android:strokeLineJoin="miter" android:strokeWidth="2"/>
<path android:fillAlpha="1" android:fillColor="#0066cc"
android:pathData="m45,7.73c-20.57,0 -37.27,16.7 -37.27,37.27 0,20.57 16.7,37.27 37.27,37.27 20.57,0 37.27,-16.7 37.27,-37.27 0,-20.57 -16.7,-37.27 -37.27,-37.27z"
android:strokeAlpha="1" android:strokeColor="#00000000"
android:strokeLineCap="butt" android:strokeLineJoin="miter" android:strokeWidth="2"/>
<path android:fillAlpha="1" android:fillColor="#ffffff"
android:pathData="M30.78,56.85h27.97v4.04h-27.97z"
android:strokeAlpha="1" android:strokeColor="#00000000" android:strokeWidth="2"/>
<path android:fillAlpha="1" android:fillColor="#ffffff"
android:pathData="m38.8,26.93 l0,12.18 -7.71,0 13.86,13.57 13.86,-13.57 -7.83,0 0,-12.18z"
android:strokeAlpha="1" android:strokeColor="#00000000" android:strokeWidth="2"/>
</vector>

View File

@ -0,0 +1,38 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="90dp"
android:height="90dp"
android:viewportWidth="90.0"
android:viewportHeight="90.0">
<path
android:pathData="M45,0.39C44.97,0.39 44.94,0.39 44.9,0.39L45,44.98L45,44.99L45,44.99L45,45L45,45L45,45L45,45L1.91,56.53C2.89,60.21 4.37,63.84 6.37,67.3L6.37,67.3C6.37,67.3 6.37,67.3 6.37,67.3C6.39,67.33 6.4,67.36 6.42,67.39L6.42,67.38C8.32,70.65 10.65,73.73 13.44,76.53C13.45,76.53 13.45,76.53 13.45,76.54C13.45,76.54 13.45,76.54 13.46,76.54C16.15,79.24 19.24,81.63 22.7,83.63C22.72,83.65 22.75,83.66 22.78,83.68L22.78,83.67C26.06,85.55 29.62,87.06 33.43,88.08L33.43,88.08C37.12,89.07 41,89.61 45,89.61L45,89.61C45.03,89.61 45.07,89.61 45.1,89.61L45.1,89.6C48.88,89.59 52.72,89.11 56.54,88.09C56.58,88.08 56.61,88.07 56.64,88.06L56.64,88.06C60.28,87.07 63.86,85.62 67.29,83.64C67.29,83.64 67.3,83.63 67.3,83.63C67.3,83.63 67.31,83.63 67.31,83.63C70.6,81.72 73.71,79.37 76.53,76.55L76.53,76.56C76.53,76.55 76.54,76.55 76.54,76.54C79.24,73.85 81.63,70.76 83.63,67.3C83.65,67.28 83.66,67.25 83.68,67.22L83.67,67.22C85.55,63.94 87.06,60.38 88.08,56.56L88.08,56.56C88.09,56.56 88.09,56.55 88.09,56.54C88.09,56.54 88.09,56.54 88.09,56.54C88.09,56.52 88.1,56.51 88.1,56.49C89.08,52.82 89.61,48.97 89.61,45L89.61,45C89.61,44.97 89.61,44.94 89.61,44.9L89.6,44.9C89.59,41.12 89.11,37.28 88.09,33.46C88.08,33.42 88.07,33.39 88.06,33.36L88.06,33.36C87.07,29.72 85.62,26.14 83.64,22.71C83.64,22.71 83.63,22.7 83.63,22.7C83.63,22.7 83.63,22.69 83.63,22.69C81.72,19.4 79.37,16.29 76.55,13.47C76.55,13.47 76.55,13.46 76.54,13.46L76.54,13.46C76.52,13.43 76.49,13.41 76.47,13.39C73.79,10.72 70.73,8.35 67.3,6.37C67.28,6.35 67.25,6.34 67.22,6.32L67.22,6.33C63.94,4.45 60.38,2.94 56.56,1.92C56.56,1.92 56.56,1.92 56.56,1.92C56.56,1.92 56.55,1.91 56.54,1.91L56.54,1.91C56.51,1.9 56.48,1.9 56.45,1.89L56.45,1.89C52.8,0.92 48.96,0.39 45,0.39L45,0.39zM45.41,44.29L45.01,44.98L45,44.99L45,44.99L45.41,44.29zM45.02,45L46.51,45L45.02,45L45.01,45L45.02,45zM45,45L45.01,45L45.01,45L46.26,45.72L45.02,45.01L45,45zM45,45L45,45L45,45.02L45,47.25L45,45.02L45,45zM45,45.01L44.99,45.03L44.04,46.68L44.99,45.02L45,45.01z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#8ab000"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="m45,7.73c-20.57,0 -37.27,16.7 -37.27,37.27 0,20.57 16.7,37.27 37.27,37.27 20.57,0 37.27,-16.7 37.27,-37.27 0,-20.57 -16.7,-37.27 -37.27,-37.27z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#0066cc"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="M30.78,56.85h27.97v4.04h-27.97z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
<path
android:pathData="m38.8,26.93 l0,12.18 -7.71,0 13.86,13.57 13.86,-13.57 -7.83,0 0,-12.18z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
</vector>

View File

@ -0,0 +1,38 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="90dp"
android:height="90dp"
android:viewportWidth="90.0"
android:viewportHeight="90.0">
<path
android:pathData="M45,0.39C44.97,0.39 44.94,0.39 44.9,0.39L45,44.98L45,44.99L45,44.99L45,45L45,45L45,45L45,45L44.98,45L0.39,44.98C0.39,48.8 0.88,52.67 1.91,56.53L1.91,56.53C1.91,56.53 1.91,56.54 1.91,56.54C1.91,56.55 1.91,56.55 1.91,56.55C2.9,60.23 4.37,63.85 6.37,67.3L6.37,67.3C6.37,67.3 6.37,67.3 6.37,67.3C6.39,67.33 6.4,67.36 6.42,67.39L6.42,67.38C8.32,70.65 10.65,73.73 13.44,76.53C13.45,76.53 13.45,76.53 13.45,76.54C13.45,76.54 13.45,76.54 13.46,76.54C16.15,79.24 19.24,81.63 22.7,83.63C22.72,83.65 22.75,83.66 22.78,83.68L22.78,83.67C26.06,85.55 29.62,87.06 33.43,88.08L33.43,88.08C37.12,89.07 41,89.61 45,89.61L45,89.61C45.03,89.61 45.07,89.61 45.1,89.61L45.1,89.6C48.88,89.59 52.72,89.11 56.54,88.09C56.58,88.08 56.61,88.07 56.64,88.06L56.64,88.06C60.28,87.07 63.86,85.62 67.29,83.64C67.29,83.64 67.3,83.63 67.3,83.63C67.3,83.63 67.31,83.63 67.31,83.63C70.6,81.72 73.71,79.37 76.53,76.55L76.53,76.56C76.53,76.55 76.54,76.55 76.54,76.54C79.24,73.85 81.63,70.76 83.63,67.3C83.65,67.28 83.66,67.25 83.68,67.22L83.67,67.22C85.55,63.94 87.06,60.38 88.08,56.56L88.08,56.56C88.09,56.56 88.09,56.55 88.09,56.54C88.09,56.54 88.09,56.54 88.09,56.54C88.09,56.52 88.1,56.51 88.1,56.49C89.08,52.82 89.61,48.97 89.61,45L89.61,45C89.61,44.97 89.61,44.94 89.61,44.9L89.6,44.9C89.59,41.12 89.11,37.28 88.09,33.46C88.08,33.42 88.07,33.39 88.06,33.36L88.06,33.36C87.07,29.72 85.62,26.14 83.64,22.71C83.64,22.71 83.63,22.7 83.63,22.7C83.63,22.7 83.63,22.69 83.63,22.69C81.72,19.4 79.37,16.29 76.55,13.47C76.55,13.47 76.55,13.46 76.54,13.46L76.54,13.46C76.52,13.43 76.49,13.41 76.47,13.39C73.79,10.72 70.73,8.35 67.3,6.37C67.28,6.35 67.25,6.34 67.22,6.32L67.22,6.33C63.94,4.45 60.38,2.94 56.56,1.92C56.56,1.92 56.56,1.92 56.56,1.92C56.56,1.92 56.55,1.91 56.54,1.91L56.54,1.91C56.51,1.9 56.48,1.9 56.45,1.89L56.45,1.89C52.8,0.92 48.96,0.39 45,0.39L45,0.39zM45.41,44.29L45.01,44.98L45,44.99L45,44.99L45.41,44.29zM45.02,45L46.51,45L45.02,45L45.01,45L45.02,45zM45,45L45.01,45L45.01,45L46.26,45.72L45.02,45.01L45,45zM45,45L45,45L45,45.02L45,47.25L45,45.02L45,45zM45,45.01L44.99,45.03L44.04,46.68L44.99,45.02L45,45.01z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#8ab000"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="m45,7.73c-20.57,0 -37.27,16.7 -37.27,37.27 0,20.57 16.7,37.27 37.27,37.27 20.57,0 37.27,-16.7 37.27,-37.27 0,-20.57 -16.7,-37.27 -37.27,-37.27z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#0066cc"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="M30.78,56.85h27.97v4.04h-27.97z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
<path
android:pathData="m38.8,26.93 l0,12.18 -7.71,0 13.86,13.57 13.86,-13.57 -7.83,0 0,-12.18z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
</vector>

View File

@ -0,0 +1,38 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="90dp"
android:height="90dp"
android:viewportWidth="90.0"
android:viewportHeight="90.0">
<path
android:pathData="M45,0.39C44.97,0.39 44.94,0.39 44.9,0.39L45,44.98L45,44.99L45,44.99L45,45L45,45L45,45L44.98,45L42.75,45L44.98,45L45,45L1.92,33.44C0.93,37.12 0.39,41 0.39,45L0.39,45C0.39,45.03 0.39,45.06 0.39,45.1L0.4,45.1C0.41,48.87 0.89,52.71 1.91,56.53L1.91,56.53C1.91,56.53 1.91,56.54 1.91,56.54C1.91,56.55 1.91,56.55 1.91,56.55C2.9,60.23 4.37,63.85 6.37,67.3L6.37,67.3C6.37,67.3 6.37,67.3 6.37,67.3C6.39,67.33 6.4,67.36 6.42,67.39L6.42,67.38C8.32,70.65 10.65,73.73 13.44,76.53C13.45,76.53 13.45,76.53 13.45,76.54C13.45,76.54 13.45,76.54 13.46,76.54C16.15,79.24 19.24,81.63 22.7,83.63C22.72,83.65 22.75,83.66 22.78,83.68L22.78,83.67C26.06,85.55 29.62,87.06 33.43,88.08L33.43,88.08C37.12,89.07 41,89.61 45,89.61L45,89.61C45.03,89.61 45.07,89.61 45.1,89.61L45.1,89.6C48.88,89.59 52.72,89.11 56.54,88.09C56.58,88.08 56.61,88.07 56.64,88.06L56.64,88.06C60.28,87.07 63.86,85.62 67.29,83.64C67.29,83.64 67.3,83.63 67.3,83.63C67.3,83.63 67.31,83.63 67.31,83.63C70.6,81.72 73.71,79.37 76.53,76.55L76.53,76.56C76.53,76.55 76.54,76.55 76.54,76.54C79.24,73.85 81.63,70.76 83.63,67.3C83.65,67.28 83.66,67.25 83.68,67.22L83.67,67.22C85.55,63.94 87.06,60.38 88.08,56.56L88.08,56.56C88.09,56.56 88.09,56.55 88.09,56.54C88.09,56.54 88.09,56.54 88.09,56.54C88.09,56.52 88.1,56.51 88.1,56.49C89.08,52.82 89.61,48.97 89.61,45L89.61,45C89.61,44.97 89.61,44.94 89.61,44.9L89.6,44.9C89.59,41.12 89.11,37.28 88.09,33.46C88.08,33.42 88.07,33.39 88.06,33.36L88.06,33.36C87.07,29.72 85.62,26.14 83.64,22.71C83.64,22.71 83.63,22.7 83.63,22.7C83.63,22.7 83.63,22.69 83.63,22.69C81.72,19.4 79.37,16.29 76.55,13.47C76.55,13.47 76.55,13.46 76.54,13.46L76.54,13.46C76.52,13.43 76.49,13.41 76.47,13.39C73.79,10.72 70.73,8.35 67.3,6.37C67.28,6.35 67.25,6.34 67.22,6.32L67.22,6.33C63.94,4.45 60.38,2.94 56.56,1.92C56.56,1.92 56.56,1.92 56.56,1.92C56.56,1.92 56.55,1.91 56.54,1.91L56.54,1.91C56.51,1.9 56.48,1.9 56.45,1.89L56.45,1.89C52.8,0.92 48.96,0.39 45,0.39L45,0.39zM45.41,44.29L45.01,44.98L45,44.99L45,44.99L45.41,44.29zM45.02,45L46.51,45L45.02,45L45.01,45L45.02,45zM45,45L45.01,45L45.01,45L46.26,45.72L45.02,45.01L45,45zM45,45L45,45L45,45.02L45,47.25L45,45.02L45,45zM45,45.01L44.99,45.03L44.04,46.68L44.99,45.02L45,45.01z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#8ab000"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="m45,7.73c-20.57,0 -37.27,16.7 -37.27,37.27 0,20.57 16.7,37.27 37.27,37.27 20.57,0 37.27,-16.7 37.27,-37.27 0,-20.57 -16.7,-37.27 -37.27,-37.27z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#0066cc"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="M30.78,56.85h27.97v4.04h-27.97z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
<path
android:pathData="m38.8,26.93 l0,12.18 -7.71,0 13.86,13.57 13.86,-13.57 -7.83,0 0,-12.18z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
</vector>

View File

@ -0,0 +1,38 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="90dp"
android:height="90dp"
android:viewportWidth="90.0"
android:viewportHeight="90.0">
<path
android:pathData="M45,0.39C44.97,0.39 44.94,0.39 44.9,0.39L45,44.98L45,44.99L45,44.99L45,45L45.01,44.98L67.32,6.38C64.02,4.47 60.42,2.95 56.56,1.92C56.56,1.92 56.56,1.92 56.56,1.92C56.56,1.92 56.55,1.91 56.54,1.91L56.54,1.91C56.51,1.9 56.48,1.9 56.45,1.89L56.45,1.89C52.8,0.92 48.96,0.39 45,0.39L45,0.39z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#8ab000"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="m45,7.73c-20.57,0 -37.27,16.7 -37.27,37.27 0,20.57 16.7,37.27 37.27,37.27 20.57,0 37.27,-16.7 37.27,-37.27 0,-20.57 -16.7,-37.27 -37.27,-37.27z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#0066cc"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="M30.78,56.85h27.97v4.04h-27.97z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
<path
android:pathData="m38.8,26.93 l0,12.18 -7.71,0 13.86,13.57 13.86,-13.57 -7.83,0 0,-12.18z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
</vector>

View File

@ -0,0 +1,38 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="90dp"
android:height="90dp"
android:viewportWidth="90.0"
android:viewportHeight="90.0">
<path
android:pathData="M45,0.39C44.97,0.39 44.94,0.39 44.9,0.39L45,44.98L45,44.99L45,45L45,45L44.98,45L42.75,45L44.98,45L45,45L44.98,44.99L6.38,22.68C4.47,25.98 2.95,29.58 1.92,33.44L1.92,33.44C1.91,33.44 1.91,33.45 1.91,33.46C1.91,33.46 1.91,33.46 1.91,33.46C1.91,33.47 1.9,33.49 1.9,33.51C1.9,33.52 1.89,33.54 1.89,33.55C0.92,37.2 0.39,41.04 0.39,45L0.39,45C0.39,45.03 0.39,45.06 0.39,45.1L0.4,45.1C0.41,48.87 0.89,52.71 1.91,56.53L1.91,56.53C1.91,56.53 1.91,56.54 1.91,56.54C1.91,56.55 1.91,56.55 1.91,56.55C2.9,60.23 4.37,63.85 6.37,67.3L6.37,67.3C6.37,67.3 6.37,67.3 6.37,67.3C6.39,67.33 6.4,67.36 6.42,67.39L6.42,67.38C8.32,70.65 10.65,73.73 13.44,76.53C13.45,76.53 13.45,76.53 13.45,76.54C13.45,76.54 13.45,76.54 13.46,76.54C16.15,79.24 19.24,81.63 22.7,83.63C22.72,83.65 22.75,83.66 22.78,83.68L22.78,83.67C26.06,85.55 29.62,87.06 33.43,88.08L33.43,88.08C37.12,89.07 41,89.61 45,89.61L45,89.61C45.03,89.61 45.07,89.61 45.1,89.61L45.1,89.6C48.88,89.59 52.72,89.11 56.54,88.09C56.58,88.08 56.61,88.07 56.64,88.06L56.64,88.06C60.28,87.07 63.86,85.62 67.29,83.64C67.29,83.64 67.3,83.63 67.3,83.63C67.3,83.63 67.31,83.63 67.31,83.63C70.6,81.72 73.71,79.37 76.53,76.55L76.53,76.56C76.53,76.55 76.54,76.55 76.54,76.54C79.24,73.85 81.63,70.76 83.63,67.3C83.65,67.28 83.66,67.25 83.68,67.22L83.67,67.22C85.55,63.94 87.06,60.38 88.08,56.56L88.08,56.56C88.09,56.56 88.09,56.55 88.09,56.54C88.09,56.54 88.09,56.54 88.09,56.54C88.09,56.52 88.1,56.51 88.1,56.49C89.08,52.82 89.61,48.97 89.61,45L89.61,45C89.61,44.97 89.61,44.94 89.61,44.9L89.6,44.9C89.59,41.12 89.11,37.28 88.09,33.46C88.08,33.42 88.07,33.39 88.06,33.36L88.06,33.36C87.07,29.72 85.62,26.14 83.64,22.71C83.64,22.71 83.63,22.7 83.63,22.7C83.63,22.7 83.63,22.69 83.63,22.69C81.72,19.4 79.37,16.29 76.55,13.47C76.55,13.47 76.55,13.46 76.54,13.46L76.54,13.46C76.52,13.43 76.49,13.41 76.47,13.39C73.79,10.72 70.73,8.35 67.3,6.37C67.28,6.35 67.25,6.34 67.22,6.32L67.22,6.33C63.94,4.45 60.38,2.94 56.56,1.92C56.56,1.92 56.56,1.92 56.56,1.92C56.56,1.92 56.55,1.91 56.54,1.91L56.54,1.91C56.51,1.9 56.48,1.9 56.45,1.89L56.45,1.89C52.8,0.92 48.96,0.39 45,0.39L45,0.39zM45.34,44.41L45.01,44.98L45,44.99L45.01,44.98L45.34,44.41zM45.02,45L46.51,45L45.02,45L45.01,45L45.01,45L45,45L45.01,45L46.05,45.61L45.02,45.01L45,45L45.74,45.74L45.01,45.01L45,45L45,45.02L45,47.25L45,45.02L45,45L45,45L45,45L45,45L45,45L45.01,45L45.02,45zM45,45.01L44.99,45.03L44.07,46.62L44.99,45.02L45,45.01z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#8ab000"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="m45,7.73c-20.57,0 -37.27,16.7 -37.27,37.27 0,20.57 16.7,37.27 37.27,37.27 20.57,0 37.27,-16.7 37.27,-37.27C82.27,24.43 65.57,7.73 45,7.73Z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#0066cc"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="M30.78,56.85h27.97v4.04h-27.97z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
<path
android:pathData="m38.8,26.93 l0,12.18 -7.71,0 13.86,13.57 13.86,-13.57 -7.83,0 0,-12.18z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
</vector>

View File

@ -0,0 +1,38 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="90dp"
android:height="90dp"
android:viewportWidth="90.0"
android:viewportHeight="90.0">
<path
android:pathData="M45,0.39C44.97,0.39 44.94,0.39 44.9,0.39L45,44.98L45,44.99L45,44.99L45,45L45,45L45,45L44.98,45L42.75,45L44.98,45L45,45L44.99,45L44.98,44.99L43.86,44.34L44.98,44.99L44.99,45L13.47,13.44C10.77,16.14 8.37,19.23 6.37,22.69L6.37,22.7C6.35,22.72 6.34,22.75 6.32,22.78L6.33,22.78C4.45,26.06 2.94,29.62 1.92,33.44L1.92,33.44C1.91,33.44 1.91,33.45 1.91,33.46C1.91,33.46 1.91,33.46 1.91,33.46C1.91,33.47 1.9,33.49 1.9,33.51C1.9,33.52 1.89,33.54 1.89,33.55C0.92,37.2 0.39,41.04 0.39,45L0.39,45C0.39,45.03 0.39,45.06 0.39,45.1L0.4,45.1C0.41,48.87 0.89,52.71 1.91,56.53L1.91,56.53C1.91,56.53 1.91,56.54 1.91,56.54C1.91,56.55 1.91,56.55 1.91,56.55C2.9,60.23 4.37,63.85 6.37,67.3L6.37,67.3C6.37,67.3 6.37,67.3 6.37,67.3C6.39,67.33 6.4,67.36 6.42,67.39L6.42,67.38C8.32,70.65 10.65,73.73 13.44,76.53C13.45,76.53 13.45,76.53 13.45,76.54C13.45,76.54 13.45,76.54 13.46,76.54C16.15,79.24 19.24,81.63 22.7,83.63C22.72,83.65 22.75,83.66 22.78,83.68L22.78,83.67C26.06,85.55 29.62,87.06 33.43,88.08L33.43,88.08C37.12,89.07 41,89.61 45,89.61L45,89.61C45.03,89.61 45.07,89.61 45.1,89.61L45.1,89.6C48.88,89.59 52.72,89.11 56.54,88.09C56.58,88.08 56.61,88.07 56.64,88.06L56.64,88.06C60.28,87.07 63.86,85.62 67.29,83.64C67.29,83.64 67.3,83.63 67.3,83.63C67.3,83.63 67.31,83.63 67.31,83.63C70.6,81.72 73.71,79.37 76.53,76.55L76.53,76.56C76.53,76.55 76.54,76.55 76.54,76.54C79.24,73.85 81.63,70.76 83.63,67.3C83.65,67.28 83.66,67.25 83.68,67.22L83.67,67.22C85.55,63.94 87.06,60.38 88.08,56.56L88.08,56.56C88.09,56.56 88.09,56.55 88.09,56.54C88.09,56.54 88.09,56.54 88.09,56.54C88.09,56.52 88.1,56.51 88.1,56.49C89.08,52.82 89.61,48.97 89.61,45L89.61,45C89.61,44.97 89.61,44.94 89.61,44.9L89.6,44.9C89.59,41.12 89.11,37.28 88.09,33.46C88.08,33.42 88.07,33.39 88.06,33.36L88.06,33.36C87.07,29.72 85.62,26.14 83.64,22.71C83.64,22.71 83.63,22.7 83.63,22.7C83.63,22.7 83.63,22.69 83.63,22.69C81.72,19.4 79.37,16.29 76.55,13.47C76.55,13.47 76.55,13.46 76.54,13.46L76.54,13.46C76.52,13.43 76.49,13.41 76.47,13.39C73.79,10.72 70.73,8.35 67.3,6.37C67.28,6.35 67.25,6.34 67.22,6.32L67.22,6.33C63.94,4.45 60.38,2.94 56.56,1.92C56.56,1.92 56.56,1.92 56.56,1.92C56.56,1.92 56.55,1.91 56.54,1.91L56.54,1.91C56.51,1.9 56.48,1.9 56.45,1.89L56.45,1.89C52.8,0.92 48.96,0.39 45,0.39L45,0.39zM45.41,44.29L45.01,44.98L45,44.99L45,44.99L45.41,44.29zM45.02,45L46.51,45L45.02,45L45.01,45L45.02,45zM45,45L45.01,45L45.01,45L46.26,45.72L45.02,45.01L45,45zM45,45L45,45L45,45.02L45,47.25L45,45.02L45,45zM45,45.01L44.99,45.03L44.04,46.68L44.99,45.02L45,45.01z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#8ab000"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="m45,7.73c-20.57,0 -37.27,16.7 -37.27,37.27 0,20.57 16.7,37.27 37.27,37.27 20.57,0 37.27,-16.7 37.27,-37.27C82.27,24.43 65.57,7.73 45,7.73Z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#0066cc"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="M30.78,56.85h27.97v4.04h-27.97z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
<path
android:pathData="m38.8,26.93 l0,12.18 -7.71,0 13.86,13.57 13.86,-13.57 -7.83,0 0,-12.18z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
</vector>

View File

@ -0,0 +1,38 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="90dp"
android:height="90dp"
android:viewportWidth="90.0"
android:viewportHeight="90.0">
<path
android:pathData="M45,0.39C44.97,0.39 44.94,0.39 44.9,0.39L45,44.98L45,44.99L45,45L45,45L45,45L44.98,44.99L43.86,44.34L44.98,44.99L45,44.99L22.71,6.36C19.41,8.26 16.3,10.62 13.47,13.44C13.47,13.45 13.47,13.45 13.46,13.45C13.46,13.45 13.46,13.45 13.46,13.46L13.46,13.46C13.43,13.48 13.41,13.5 13.39,13.53C10.72,16.21 8.35,19.26 6.37,22.69L6.37,22.7C6.35,22.72 6.34,22.75 6.32,22.78L6.33,22.78C4.45,26.06 2.94,29.62 1.92,33.44L1.92,33.44C1.91,33.44 1.91,33.45 1.91,33.46C1.91,33.46 1.91,33.46 1.91,33.46C1.91,33.47 1.9,33.49 1.9,33.51C1.9,33.52 1.89,33.54 1.89,33.55C0.92,37.2 0.39,41.04 0.39,45L0.39,45C0.39,45.03 0.39,45.06 0.39,45.1L0.4,45.1C0.41,48.87 0.89,52.71 1.91,56.53L1.91,56.53C1.91,56.53 1.91,56.54 1.91,56.54C1.91,56.55 1.91,56.55 1.91,56.55C2.9,60.23 4.37,63.85 6.37,67.3L6.37,67.3C6.37,67.3 6.37,67.3 6.37,67.3C6.39,67.33 6.4,67.36 6.42,67.39L6.42,67.38C8.32,70.65 10.65,73.73 13.44,76.53C13.45,76.53 13.45,76.53 13.45,76.54C13.45,76.54 13.45,76.54 13.46,76.54C16.15,79.24 19.24,81.63 22.7,83.63C22.72,83.65 22.75,83.66 22.78,83.68L22.78,83.67C26.06,85.55 29.62,87.06 33.43,88.08L33.43,88.08C37.12,89.07 41,89.61 45,89.61L45,89.61C45.03,89.61 45.07,89.61 45.1,89.61L45.1,89.6C48.88,89.59 52.72,89.11 56.54,88.09C56.58,88.08 56.61,88.07 56.64,88.06L56.64,88.06C60.28,87.07 63.86,85.62 67.29,83.64C67.29,83.64 67.3,83.63 67.3,83.63C67.3,83.63 67.31,83.63 67.31,83.63C70.6,81.72 73.71,79.37 76.53,76.55L76.53,76.56C76.53,76.55 76.54,76.55 76.54,76.54C79.24,73.85 81.63,70.76 83.63,67.3C83.65,67.28 83.66,67.25 83.68,67.22L83.67,67.22C85.55,63.94 87.06,60.38 88.08,56.56L88.08,56.56C88.09,56.56 88.09,56.55 88.09,56.54C88.09,56.54 88.09,56.54 88.09,56.54C88.09,56.52 88.1,56.51 88.1,56.49C89.08,52.82 89.61,48.97 89.61,45L89.61,45C89.61,44.97 89.61,44.94 89.61,44.9L89.6,44.9C89.59,41.12 89.11,37.28 88.09,33.46C88.08,33.42 88.07,33.39 88.06,33.36L88.06,33.36C87.07,29.72 85.62,26.14 83.64,22.71C83.64,22.71 83.63,22.7 83.63,22.7C83.63,22.7 83.63,22.69 83.63,22.69C81.72,19.4 79.37,16.29 76.55,13.47C76.55,13.47 76.55,13.46 76.54,13.46L76.54,13.46C76.52,13.43 76.49,13.41 76.47,13.39C73.79,10.72 70.73,8.35 67.3,6.37C67.28,6.35 67.25,6.34 67.22,6.32L67.22,6.33C63.94,4.45 60.38,2.94 56.56,1.92C56.56,1.92 56.56,1.92 56.56,1.92C56.56,1.92 56.55,1.91 56.54,1.91L56.54,1.91C56.51,1.9 56.48,1.9 56.45,1.89L56.45,1.89C52.8,0.92 48.96,0.39 45,0.39L45,0.39zM45.41,44.29L45.01,44.98L45,44.99L45.01,44.98L45.41,44.29zM45.02,45L46.51,45L45.02,45L45.01,45L45.02,45zM44.98,45L45,45L44.98,45L42.75,45L44.98,45zM45,45L45,45L45,45.02L45,47.25L45,45.02L45,45zM45.01,45L46.26,45.72L45.02,45.01L45.01,45zM45,45.01L44.99,45.03L44.04,46.68L44.99,45.02L45,45.01z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#8ab000"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="m45,7.73c-20.57,0 -37.27,16.7 -37.27,37.27 0,20.57 16.7,37.27 37.27,37.27 20.57,0 37.27,-16.7 37.27,-37.27C82.27,24.43 65.57,7.73 45,7.73Z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#0066cc"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="M30.78,56.85h27.97v4.04h-27.97z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
<path
android:pathData="m38.8,26.93 l0,12.18 -7.71,0 13.86,13.57 13.86,-13.57 -7.83,0 0,-12.18z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
</vector>

View File

@ -0,0 +1,38 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="90dp"
android:height="90dp"
android:viewportWidth="90.0"
android:viewportHeight="90.0">
<path
android:pathData="M45,0.39C44.97,0.39 44.94,0.39 44.9,0.39L45,44.98L45,44.99L33.47,1.91C29.79,2.89 26.17,4.36 22.71,6.36C22.69,6.37 22.67,6.39 22.65,6.4C22.64,6.41 22.62,6.41 22.61,6.42C19.35,8.31 16.27,10.65 13.47,13.44C13.47,13.45 13.47,13.45 13.46,13.45C13.46,13.45 13.46,13.45 13.46,13.46L13.46,13.46C13.43,13.48 13.41,13.5 13.39,13.53C10.72,16.21 8.35,19.26 6.37,22.69L6.37,22.7C6.35,22.72 6.34,22.75 6.32,22.78L6.33,22.78C4.45,26.06 2.94,29.62 1.92,33.44L1.92,33.44C1.91,33.44 1.91,33.45 1.91,33.46C1.91,33.46 1.91,33.46 1.91,33.46C1.91,33.47 1.9,33.49 1.9,33.51C1.9,33.52 1.89,33.54 1.89,33.55C0.92,37.2 0.39,41.04 0.39,45L0.39,45C0.39,45.03 0.39,45.06 0.39,45.1L0.4,45.1C0.41,48.87 0.89,52.71 1.91,56.53L1.91,56.53C1.91,56.53 1.91,56.54 1.91,56.54C1.91,56.55 1.91,56.55 1.91,56.55C2.9,60.23 4.37,63.85 6.37,67.3L6.37,67.3C6.37,67.3 6.37,67.3 6.37,67.3C6.39,67.33 6.4,67.36 6.42,67.39L6.42,67.38C8.32,70.65 10.65,73.73 13.44,76.53C13.45,76.53 13.45,76.53 13.45,76.54C13.45,76.54 13.45,76.54 13.46,76.54C16.15,79.24 19.24,81.63 22.7,83.63C22.72,83.65 22.75,83.66 22.78,83.68L22.78,83.67C26.06,85.55 29.62,87.06 33.43,88.08L33.43,88.08C37.12,89.07 41,89.61 45,89.61L45,89.61C45.03,89.61 45.07,89.61 45.1,89.61L45.1,89.6C48.88,89.59 52.72,89.11 56.54,88.09C56.58,88.08 56.61,88.07 56.64,88.06L56.64,88.06C60.28,87.07 63.86,85.62 67.29,83.64C67.29,83.64 67.3,83.63 67.3,83.63C67.3,83.63 67.31,83.63 67.31,83.63C70.6,81.72 73.71,79.37 76.53,76.55L76.53,76.56C76.53,76.55 76.54,76.55 76.54,76.54C79.24,73.85 81.63,70.76 83.63,67.3C83.65,67.28 83.66,67.25 83.68,67.22L83.67,67.22C85.55,63.94 87.06,60.38 88.08,56.56L88.08,56.56C88.09,56.56 88.09,56.55 88.09,56.54C88.09,56.54 88.09,56.54 88.09,56.54C88.09,56.52 88.1,56.51 88.1,56.49C89.08,52.82 89.61,48.97 89.61,45L89.61,45C89.61,44.97 89.61,44.94 89.61,44.9L89.6,44.9C89.59,41.12 89.11,37.28 88.09,33.46C88.08,33.42 88.07,33.39 88.06,33.36L88.06,33.36C87.07,29.72 85.62,26.14 83.64,22.71C83.64,22.71 83.63,22.7 83.63,22.7C83.63,22.7 83.63,22.69 83.63,22.69C81.72,19.4 79.37,16.29 76.55,13.47C76.55,13.47 76.55,13.46 76.54,13.46L76.54,13.46C76.52,13.43 76.49,13.41 76.47,13.39C73.79,10.72 70.73,8.35 67.3,6.37C67.28,6.35 67.25,6.34 67.22,6.32L67.22,6.33C63.94,4.45 60.38,2.94 56.56,1.92C56.56,1.92 56.56,1.92 56.56,1.92C56.56,1.92 56.55,1.91 56.54,1.91L56.54,1.91C56.51,1.9 56.48,1.9 56.45,1.89L56.45,1.89C52.8,0.92 48.96,0.39 45,0.39L45,0.39zM43.86,44.34L44.98,44.99L44.99,45L44.98,44.99L43.86,44.34zM45.02,45L46.51,45L45.02,45L45.01,45L45.02,45zM44.98,45L45,45L45,45L45,45L44.98,45L42.75,45L44.98,45zM45,45L45.01,45L46.26,45.72L45.02,45.01L45,45zM45,45L45,45L45,45.02L45,47.25L45,45.02L45,45zM45,45.01L44.99,45.03L44.04,46.68L44.99,45.02L45,45.01z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#8ab000"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="m45,7.73c-20.57,0 -37.27,16.7 -37.27,37.27 0,20.57 16.7,37.27 37.27,37.27 20.57,0 37.27,-16.7 37.27,-37.27C82.27,24.43 65.57,7.73 45,7.73Z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#0066cc"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="M30.78,56.85h27.97v4.04h-27.97z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
<path
android:pathData="m38.8,26.93 l0,12.18 -7.71,0 13.86,13.57 13.86,-13.57 -7.83,0 0,-12.18z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
</vector>

View File

@ -0,0 +1,38 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="90dp"
android:height="90dp"
android:viewportWidth="90.0"
android:viewportHeight="90.0">
<path
android:pathData="M45,0.39C44.97,0.39 44.94,0.39 44.9,0.39L44.9,0.4C41.12,0.41 37.28,0.89 33.45,1.91L33.46,1.91C33.42,1.92 33.39,1.93 33.36,1.94C29.72,2.93 26.14,4.38 22.71,6.36C22.69,6.37 22.67,6.39 22.65,6.4C22.64,6.41 22.62,6.41 22.61,6.42C19.35,8.31 16.27,10.65 13.47,13.44C13.47,13.45 13.47,13.45 13.46,13.45C13.46,13.45 13.46,13.45 13.46,13.46L13.46,13.46C13.43,13.48 13.41,13.5 13.39,13.53C10.72,16.21 8.35,19.26 6.37,22.69L6.37,22.7C6.35,22.72 6.34,22.75 6.32,22.78L6.33,22.78C4.45,26.06 2.94,29.62 1.92,33.44L1.92,33.44C1.91,33.44 1.91,33.45 1.91,33.46C1.91,33.46 1.91,33.46 1.91,33.46C1.91,33.47 1.9,33.49 1.9,33.51C1.9,33.52 1.89,33.54 1.89,33.55C0.92,37.2 0.39,41.04 0.39,45L0.39,45C0.39,45.03 0.39,45.06 0.39,45.1L0.4,45.1C0.41,48.87 0.89,52.71 1.91,56.53L1.91,56.53C1.91,56.53 1.91,56.54 1.91,56.54C1.91,56.55 1.91,56.55 1.91,56.55C2.9,60.23 4.37,63.85 6.37,67.3L6.37,67.3C6.37,67.3 6.37,67.3 6.37,67.3C6.39,67.33 6.4,67.36 6.42,67.39L6.42,67.38C8.32,70.65 10.65,73.73 13.44,76.53C13.45,76.53 13.45,76.53 13.45,76.54C13.45,76.54 13.45,76.54 13.46,76.54C16.15,79.24 19.24,81.63 22.7,83.63C22.72,83.65 22.75,83.66 22.78,83.68L22.78,83.67C26.06,85.55 29.62,87.06 33.43,88.08L33.43,88.08C37.12,89.07 41,89.61 45,89.61L45,89.61C45.03,89.61 45.07,89.61 45.1,89.61L45.1,89.6C48.88,89.59 52.72,89.11 56.54,88.09C56.58,88.08 56.61,88.07 56.64,88.06L56.64,88.06C60.28,87.07 63.86,85.62 67.29,83.64C67.29,83.64 67.3,83.63 67.3,83.63C67.3,83.63 67.31,83.63 67.31,83.63C70.6,81.72 73.71,79.37 76.53,76.55L76.53,76.56C76.53,76.55 76.54,76.55 76.54,76.54C79.24,73.85 81.63,70.76 83.63,67.3C83.65,67.28 83.66,67.25 83.68,67.22L83.67,67.22C85.55,63.94 87.06,60.38 88.08,56.56L88.08,56.56C88.09,56.56 88.09,56.55 88.09,56.54C88.09,56.54 88.09,56.54 88.09,56.54C88.09,56.52 88.1,56.51 88.1,56.49C89.08,52.82 89.61,48.97 89.61,45L89.61,45C89.61,44.97 89.61,44.94 89.61,44.9L89.6,44.9C89.59,41.12 89.11,37.28 88.09,33.46C88.08,33.42 88.07,33.39 88.06,33.36L88.06,33.36C87.07,29.72 85.62,26.14 83.64,22.71C83.64,22.71 83.63,22.7 83.63,22.7C83.63,22.7 83.63,22.69 83.63,22.69C81.72,19.4 79.37,16.29 76.55,13.47C76.55,13.47 76.55,13.46 76.54,13.46L76.54,13.46C76.52,13.43 76.49,13.41 76.47,13.39C73.79,10.72 70.73,8.35 67.3,6.37C67.28,6.35 67.25,6.34 67.22,6.32L67.22,6.33C63.94,4.45 60.38,2.94 56.56,1.92C56.56,1.92 56.56,1.92 56.56,1.92C56.56,1.92 56.55,1.91 56.54,1.91L56.54,1.91C56.51,1.9 56.48,1.9 56.45,1.89L56.45,1.89C52.8,0.92 48.97,0.39 45.01,0.39C45.01,0.39 45.01,0.39 45,0.39L45,0.39zM45,43.47L45,44.98L45,44.99L45,44.98L45,43.47zM45.41,44.29L45.01,44.98L45,44.99L45.01,44.98L45.41,44.29zM43.86,44.34L44.98,44.99L44.99,45L44.98,44.99L43.86,44.34zM45.02,45L46.51,45L45.02,45L45.01,45L45.02,45zM44.98,45L45,45L45,45L45,45L44.98,45L42.75,45L44.98,45zM45,45L45.01,45L46.26,45.72L45.02,45.01L45,45zM45,45L45,45L45,45.02L45,47.25L45,45.02L45,45zM45,45.01L44.99,45.03L44.04,46.68L44.99,45.02L45,45.01z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#8ab000"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="m45,7.73c-20.57,0 -37.27,16.7 -37.27,37.27 0,20.57 16.7,37.27 37.27,37.27 20.57,0 37.27,-16.7 37.27,-37.27C82.27,24.43 65.57,7.73 45,7.73Z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#0066cc"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="M30.78,56.85h27.97v4.04h-27.97z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
<path
android:pathData="m38.8,26.93 l0,12.18 -7.71,0 13.86,13.57 13.86,-13.57 -7.83,0 0,-12.18z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
</vector>

View File

@ -0,0 +1,38 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="90dp"
android:height="90dp"
android:viewportWidth="90.0"
android:viewportHeight="90.0">
<path
android:pathData="M45,0.39C44.97,0.39 44.94,0.39 44.9,0.39L45,44.98L45,44.99L45,45L45,45L45,45L45,45L45,45L76.55,13.47C73.85,10.77 70.77,8.37 67.3,6.37C67.28,6.35 67.25,6.34 67.22,6.32L67.22,6.33C63.94,4.45 60.38,2.94 56.56,1.92C56.56,1.92 56.56,1.92 56.56,1.92C56.56,1.92 56.55,1.91 56.54,1.91L56.54,1.91C56.51,1.9 56.48,1.9 56.45,1.89L56.45,1.89C52.8,0.92 48.96,0.39 45,0.39L45,0.39zM45.41,44.29L45.01,44.98L45,44.99L45.41,44.29z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#8ab000"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="m45,7.73c-20.57,0 -37.27,16.7 -37.27,37.27 0,20.57 16.7,37.27 37.27,37.27 20.57,0 37.27,-16.7 37.27,-37.27 0,-20.57 -16.7,-37.27 -37.27,-37.27z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#0066cc"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="M30.78,56.85h27.97v4.04h-27.97z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
<path
android:pathData="m38.8,26.93 l0,12.18 -7.71,0 13.86,13.57 13.86,-13.57 -7.83,0 0,-12.18z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
</vector>

View File

@ -0,0 +1,38 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="90dp"
android:height="90dp"
android:viewportWidth="90.0"
android:viewportHeight="90.0">
<path
android:pathData="M45,0.39C44.97,0.39 44.94,0.39 44.9,0.39L45,44.98L45,44.99L45,45L45,45L45,45L83.64,22.71C81.73,19.41 79.38,16.3 76.55,13.47C76.55,13.47 76.55,13.46 76.54,13.46L76.54,13.46C76.52,13.43 76.49,13.41 76.47,13.39C73.79,10.72 70.73,8.35 67.3,6.37C67.28,6.35 67.25,6.34 67.22,6.32L67.22,6.33C63.94,4.45 60.38,2.94 56.56,1.92C56.56,1.92 56.56,1.92 56.56,1.92C56.56,1.92 56.55,1.91 56.54,1.91L56.54,1.91C56.51,1.9 56.48,1.9 56.45,1.89L56.45,1.89C52.8,0.92 48.96,0.39 45,0.39L45,0.39zM45.41,44.29L45.01,44.98L45,44.99L45.41,44.29z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#8ab000"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="m45,7.73c-20.57,0 -37.27,16.7 -37.27,37.27 0,20.57 16.7,37.27 37.27,37.27 20.57,0 37.27,-16.7 37.27,-37.27 0,-20.57 -16.7,-37.27 -37.27,-37.27z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#0066cc"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="M30.78,56.85h27.97v4.04h-27.97z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
<path
android:pathData="m38.8,26.93 l0,12.18 -7.71,0 13.86,13.57 13.86,-13.57 -7.83,0 0,-12.18z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
</vector>

View File

@ -0,0 +1,38 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="90dp"
android:height="90dp"
android:viewportWidth="90.0"
android:viewportHeight="90.0">
<path
android:pathData="M45,0.39C44.97,0.39 44.94,0.39 44.9,0.39L45,44.98L45,44.99L45,45L45,45L88.09,33.47C87.11,29.79 85.64,26.17 83.64,22.71C83.64,22.71 83.63,22.7 83.63,22.7C83.63,22.7 83.63,22.69 83.63,22.69C81.72,19.4 79.37,16.29 76.55,13.47C76.55,13.47 76.55,13.46 76.54,13.46L76.54,13.46C76.52,13.43 76.49,13.41 76.47,13.39C73.79,10.72 70.73,8.35 67.3,6.37C67.28,6.35 67.25,6.34 67.22,6.32L67.22,6.33C63.94,4.45 60.38,2.94 56.56,1.92C56.56,1.92 56.56,1.92 56.56,1.92C56.56,1.92 56.55,1.91 56.54,1.91L56.54,1.91C56.51,1.9 56.48,1.9 56.45,1.89L56.45,1.89C52.8,0.92 48.96,0.39 45,0.39L45,0.39zM45.41,44.29L45.01,44.98L45,44.99L45.41,44.29z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#8ab000"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="m45,7.73c-20.57,0 -37.27,16.7 -37.27,37.27 0,20.57 16.7,37.27 37.27,37.27 20.57,0 37.27,-16.7 37.27,-37.27 0,-20.57 -16.7,-37.27 -37.27,-37.27z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#0066cc"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="M30.78,56.85h27.97v4.04h-27.97z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
<path
android:pathData="m38.8,26.93 l0,12.18 -7.71,0 13.86,13.57 13.86,-13.57 -7.83,0 0,-12.18z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
</vector>

View File

@ -0,0 +1,38 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="90dp"
android:height="90dp"
android:viewportWidth="90.0"
android:viewportHeight="90.0">
<path
android:pathData="M45,0.39C44.97,0.39 44.94,0.39 44.9,0.39L45,44.98L45,44.99L45,45L45,45L45.02,45L89.61,45.02C89.61,41.2 89.12,37.32 88.09,33.46C88.08,33.42 88.07,33.39 88.06,33.36L88.06,33.36C87.07,29.72 85.62,26.14 83.64,22.71C83.64,22.71 83.63,22.7 83.63,22.7C83.63,22.7 83.63,22.69 83.63,22.69C81.72,19.4 79.37,16.29 76.55,13.47C76.55,13.47 76.55,13.46 76.54,13.46L76.54,13.46C76.52,13.43 76.49,13.41 76.47,13.39C73.79,10.72 70.73,8.35 67.3,6.37C67.28,6.35 67.25,6.34 67.22,6.32L67.22,6.33C63.94,4.45 60.38,2.94 56.56,1.92C56.56,1.92 56.56,1.92 56.56,1.92C56.56,1.92 56.55,1.91 56.54,1.91L56.54,1.91C56.51,1.9 56.48,1.9 56.45,1.89L56.45,1.89C52.8,0.92 48.96,0.39 45,0.39L45,0.39zM45.41,44.29L45.01,44.98L45,44.99L45.41,44.29z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#8ab000"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="m45,7.73c-20.57,0 -37.27,16.7 -37.27,37.27 0,20.57 16.7,37.27 37.27,37.27 20.57,0 37.27,-16.7 37.27,-37.27 0,-20.57 -16.7,-37.27 -37.27,-37.27z"
android:strokeLineCap="butt"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#0066cc"
android:strokeWidth="2"
android:strokeLineJoin="miter"
android:strokeAlpha="1"/>
<path
android:pathData="M30.78,56.85h27.97v4.04h-27.97z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
<path
android:pathData="m38.8,26.93 l0,12.18 -7.71,0 13.86,13.57 13.86,-13.57 -7.83,0 0,-12.18z"
android:fillAlpha="1"
android:strokeColor="#00000000"
android:fillColor="#ffffff"
android:strokeWidth="2"
android:strokeAlpha="1"/>
</vector>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app__newly_added"
android:textSize="12sp"
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:paddingStart="10dp"
android:paddingLeft="10dp"
android:paddingEnd="10dp"
android:paddingRight="10dp"
android:textAllCaps="true"
android:textColor="@color/fdroid_blue"
android:background="@drawable/app_tag_new_background"
/>

View File

@ -30,7 +30,7 @@
app:srcCompat="@drawable/ic_back_black_24dp" /> app:srcCompat="@drawable/ic_back_black_24dp" />
<EditText <EditText
android:layout_width="match_parent" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
@ -38,7 +38,10 @@
app:layout_constraintEnd_toStartOf="@+id/clear" app:layout_constraintEnd_toStartOf="@+id/clear"
android:padding="12dp" android:padding="12dp"
android:id="@+id/search" android:id="@+id/search"
android:imeOptions="actionSearch"
android:inputType="text"
android:hint="@string/search_hint" android:hint="@string/search_hint"
android:maxLines="1"
android:background="@android:color/transparent" android:background="@android:color/transparent"
android:textSize="18sp" /> android:textSize="18sp" />

View File

@ -7,7 +7,7 @@
android:paddingBottom="2dp" android:paddingBottom="2dp"
android:clipToPadding="false"> android:clipToPadding="false">
<ImageView <org.fdroid.fdroid.views.apps.FeatureImage
android:id="@+id/featured_image" android:id="@+id/featured_image"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="120dp" android:layout_height="120dp"
@ -40,15 +40,17 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="12dp"> android:layout_margin="12dp">
<!-- Ignore ContentDescription because it is kind of meaningless to have TTS read out "App icon"
when it will inevitably read out the name of the app straight after. -->
<ImageView <ImageView
android:id="@+id/icon" android:id="@+id/icon"
android:contentDescription="@string/app_icon"
android:layout_width="48dp" android:layout_width="48dp"
android:layout_height="48dp" android:layout_height="48dp"
tools:src="@drawable/ic_launcher" tools:src="@drawable/ic_launcher"
android:scaleType="fitCenter" android:scaleType="fitCenter"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
<TextView <TextView
tools:text="F-Droid An application summary which takes up too much space and must ellipsize, perhaps after wrapping to a new line" tools:text="F-Droid An application summary which takes up too much space and must ellipsize, perhaps after wrapping to a new line"
@ -64,19 +66,14 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<TextView <include layout="@layout/app_status_new"
tools:text="Recently added"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/status" android:id="@+id/new_tag"
android:lines="1"
android:textSize="12sp"
android:ellipsize="end"
android:layout_marginLeft="16dp" android:layout_marginLeft="16dp"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
app:layout_constraintTop_toBottomOf="@+id/summary" app:layout_constraintTop_toBottomOf="@+id/summary"
app:layout_constraintStart_toEndOf="@+id/icon" app:layout_constraintStart_toEndOf="@+id/icon"
android:textStyle="italic"
android:layout_marginTop="8dp" /> android:layout_marginTop="8dp" />
</android.support.constraint.ConstraintLayout> </android.support.constraint.ConstraintLayout>

View File

@ -10,22 +10,24 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="8dp"> android:layout_margin="8dp">
<!-- Ignore ContentDescription because it is kind of meaningless to have TTS read out "App icon"
when it will inevitably read out the name of the app straight after. -->
<ImageView <ImageView
android:id="@+id/icon" android:id="@+id/icon"
android:contentDescription="@string/app_icon"
android:layout_width="48dp" android:layout_width="48dp"
android:layout_height="48dp" android:layout_height="48dp"
tools:src="@drawable/ic_launcher" tools:src="@drawable/ic_launcher"
android:scaleType="fitCenter" android:scaleType="fitCenter"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
<TextView <TextView
tools:text="F-Droid An application summary which takes up too much space and must ellipsize" tools:text="F-Droid An application summary which takes up too much space and must ellipsize"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/summary" android:id="@+id/summary"
android:lines="4" android:maxLines="4"
android:textSize="13sp" android:textSize="13sp"
android:ellipsize="end" android:ellipsize="end"
android:layout_marginLeft="8dp" android:layout_marginLeft="8dp"
@ -34,18 +36,14 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<TextView <include layout="@layout/app_status_new"
tools:text="Recently added"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/status" android:id="@+id/new_tag"
android:lines="1" android:layout_marginTop="4dp"
android:textSize="12sp"
android:ellipsize="end"
app:layout_constraintTop_toBottomOf="@+id/summary" app:layout_constraintTop_toBottomOf="@+id/summary"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="@+id/summary"
android:textStyle="italic" app:layout_constraintLeft_toLeftOf="@+id/summary" />
android:layout_marginTop="4dp" />
</android.support.constraint.ConstraintLayout> </android.support.constraint.ConstraintLayout>

View File

@ -10,9 +10,10 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_margin="8dp"> android:layout_margin="8dp">
<!-- Ignore ContentDescription because it is kind of meaningless to have TTS read out "App icon"
when it will inevitably read out the name of the app straight after. -->
<ImageView <ImageView
android:id="@+id/icon" android:id="@+id/icon"
android:contentDescription="@string/app_icon"
android:layout_width="96dip" android:layout_width="96dip"
android:layout_height="96dip" android:layout_height="96dip"
tools:src="@drawable/ic_launcher" tools:src="@drawable/ic_launcher"
@ -25,7 +26,8 @@
android:layout_marginBottom="8dp" android:layout_marginBottom="8dp"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
<TextView <TextView
tools:text="F-Droid An application summary which takes up too much space and must ellipsize" tools:text="F-Droid An application summary which takes up too much space and must ellipsize"
@ -40,18 +42,14 @@
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
android:layout_marginTop="8dp" /> android:layout_marginTop="8dp" />
<TextView <include layout="@layout/app_status_new"
tools:text="Recently added"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/status" android:id="@+id/new_tag"
android:lines="1" android:layout_marginTop="4dp"
android:textSize="12sp" app:layout_constraintTop_toBottomOf="@+id/summary"
android:ellipsize="end" app:layout_constraintStart_toStartOf="@+id/summary"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="@+id/summary" />
app:layout_constraintStart_toStartOf="parent"
android:textStyle="italic"
android:layout_marginTop="8dp" />
</android.support.constraint.ConstraintLayout> </android.support.constraint.ConstraintLayout>

View File

@ -10,15 +10,17 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_margin="8dp"> android:layout_margin="8dp">
<!-- Ignore ContentDescription because it is kind of meaningless to have TTS read out "App icon"
when it will inevitably read out the name of the app straight after. -->
<ImageView <ImageView
android:id="@+id/icon" android:id="@+id/icon"
android:contentDescription="@string/app_icon"
android:layout_width="48dp" android:layout_width="48dp"
android:layout_height="48dp" android:layout_height="48dp"
tools:src="@drawable/ic_launcher" tools:src="@drawable/ic_launcher"
android:scaleType="fitCenter" android:scaleType="fitCenter"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
<TextView <TextView
tools:text="F-Droid An application summary which takes up too much space and must ellipsize" tools:text="F-Droid An application summary which takes up too much space and must ellipsize"
@ -35,20 +37,14 @@
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
tools:layout_editor_absoluteY="8dp" /> tools:layout_editor_absoluteY="8dp" />
<TextView <include layout="@layout/app_status_new"
tools:text="Recently added"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/status" android:id="@+id/new_tag"
android:lines="1" android:layout_marginTop="4dp"
android:textSize="12sp"
android:ellipsize="end"
android:layout_marginLeft="16dp"
android:layout_marginStart="16dp"
app:layout_constraintTop_toBottomOf="@+id/summary" app:layout_constraintTop_toBottomOf="@+id/summary"
app:layout_constraintStart_toEndOf="@+id/icon" app:layout_constraintStart_toStartOf="@+id/summary"
android:textStyle="italic" app:layout_constraintLeft_toLeftOf="@+id/summary" />
android:layout_marginTop="8dp" />
</android.support.constraint.ConstraintLayout> </android.support.constraint.ConstraintLayout>

View File

@ -25,13 +25,13 @@
app:contentScrim="?attr/colorPrimary" app:contentScrim="?attr/colorPrimary"
app:layout_scrollFlags="scroll|exitUntilCollapsed"> app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView <org.fdroid.fdroid.views.apps.FeatureImage
android:id="@+id/feature_graphic" android:id="@+id/feature_graphic"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:scaleType="centerCrop" android:scaleType="centerCrop"
android:fitsSystemWindows="true" android:fitsSystemWindows="true"
android:src="@drawable/feature_placeholder" tools:src="@drawable/feature_placeholder"
app:layout_collapseMode="parallax" /> app:layout_collapseMode="parallax" />
<android.support.v7.widget.Toolbar <android.support.v7.widget.Toolbar

View File

@ -6,18 +6,20 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<!-- Ignore ContentDescription because it is kind of meaningless to have TTS read out "App icon"
when it will inevitably read out the name of the app straight after. -->
<ImageView <ImageView
android:id="@+id/icon" android:id="@+id/icon"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
android:contentDescription="@string/app_icon"
android:layout_width="48dp" android:layout_width="48dp"
android:layout_height="48dp" android:layout_height="48dp"
tools:src="@drawable/ic_launcher" tools:src="@drawable/ic_launcher"
android:scaleType="fitCenter" android:scaleType="fitCenter"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginLeft="16dp" android:layout_marginLeft="16dp"
android:layout_marginTop="8dp" /> android:layout_marginTop="8dp"
tools:ignore="ContentDescription" />
<TextView <TextView
android:id="@+id/app_name" android:id="@+id/app_name"
@ -53,11 +55,14 @@
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:layout_marginLeft="8dp" /> android:layout_marginLeft="8dp" />
<Button <ImageView
android:id="@+id/install" android:id="@+id/install"
android:background="@drawable/download_button" tools:src="@drawable/ic_download"
android:layout_width="48dp" android:scaleType="fitXY"
android:layout_height="48dp" android:contentDescription="@string/menu_install"
android:layout_width="64dp"
android:layout_height="64dp"
android:elevation="2dp"
android:padding="4dp" android:padding="4dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:layout_marginRight="16dp" android:layout_marginRight="16dp"

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app__newly_added"
android:textSize="12sp"
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:paddingStart="10dp"
android:paddingLeft="10dp"
android:paddingEnd="10dp"
android:paddingRight="10dp"
android:textColor="@color/fdroid_blue"
android:background="@drawable/app_tag_new_background"
/>

View File

@ -24,6 +24,7 @@
<Button <Button
android:id="@+id/button" android:id="@+id/button"
tools:text="View all 10" tools:text="View all 10"
style="@style/DetailsSecondaryButtonStyle"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="0dp" android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.FloatingActionButton
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/btn_search"
android:src="@drawable/ic_search_white"
android:contentDescription="@string/menu_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginBottom="@dimen/fab_margin"
android:layout_marginEnd="@dimen/fab_margin"
android:layout_marginRight="@dimen/fab_margin" />

View File

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="4dp"
android:paddingTop="8dp"
android:paddingBottom="8dp">
<!-- Ignore ContentDescription because it is kind of meaningless to have TTS read out "App icon"
when it will inevitably read out the name of the app straight after. -->
<ImageView
android:id="@+id/icon"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_width="48dp"
android:layout_height="48dp"
tools:src="@drawable/ic_launcher"
android:scaleType="fitCenter"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginTop="8dp"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/app_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
tools:text="F-Droid Application manager with a long name that will wrap and then ellipsize"
android:textSize="18sp"
android:textColor="#424242"
android:maxLines="2"
android:ellipsize="end"
app:layout_constraintStart_toEndOf="@+id/icon"
app:layout_constraintTop_toTopOf="@+id/icon"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp" />
<TextView
android:id="@+id/installed_version"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
tools:text="Version 4.7.3 (recommended)"
android:textStyle="italic"
android:textSize="14sp"
android:textColor="#424242"
android:maxLines="1"
android:ellipsize="end"
android:fontFamily="sans-serif-light"
app:layout_constraintTop_toBottomOf="@+id/app_name"
app:layout_constraintStart_toEndOf="@+id/icon"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp" />
<TextView
android:id="@+id/ignored_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
tools:text="Updates ignored"
android:textSize="14sp"
android:maxLines="1"
android:ellipsize="end"
app:layout_constraintTop_toBottomOf="@+id/installed_version"
app:layout_constraintStart_toEndOf="@+id/icon"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp" />
</android.support.constraint.ConstraintLayout>

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:theme="?attr/actionBarTheme"
app:popupTheme="?attr/actionBarPopupTheme" />
<android.support.v7.widget.RecyclerView
android:id="@+id/app_list"
android:layout_width="0dp"
android:layout_height="0dp"
tools:listitem="@layout/installed_app_list_item"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/toolbar"
android:scrollbars="vertical" />
<!--
Commented out until the long press functionality is implemented.
<TextView
android:id="@+id/helpText"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:padding="16dp"
android:textAlignment="center"
android:textColor="#424242"
android:textSize="14sp"
android:text="Tap and hold on an app for more options"
app:layout_constraintHorizontal_bias="0.0"
android:layout_marginBottom="-1dp" />-->
</android.support.constraint.ConstraintLayout>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical" android:orientation="vertical"
@ -16,4 +17,6 @@
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
android:scrollbars="vertical" /> android:scrollbars="vertical" />
</LinearLayout> <include layout="@layout/fab_search" />
</android.support.design.widget.CoordinatorLayout>

View File

@ -54,6 +54,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Find people near me" android:text="Find people near me"
style="@style/DetailsSecondaryButtonStyle"
app:layout_constraintTop_toBottomOf="@+id/text2" app:layout_constraintTop_toBottomOf="@+id/text2"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical" android:orientation="vertical"
@ -23,4 +24,6 @@
</android.support.v4.widget.SwipeRefreshLayout> </android.support.v4.widget.SwipeRefreshLayout>
</LinearLayout> <include layout="@layout/fab_search" />
</android.support.design.widget.CoordinatorLayout>

View File

@ -15,6 +15,7 @@
<Button <Button
android:id="@+id/update_all_button" android:id="@+id/update_all_button"
android:text="@string/my_apps_btn_update_all" android:text="@string/my_apps_btn_update_all"
style="@style/DetailsSecondaryButtonStyle"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"

View File

@ -4,26 +4,26 @@
<item <item
android:title="@string/main_menu__latest_apps" android:title="@string/main_menu__latest_apps"
android:icon="@drawable/ic_overview" android:icon="@drawable/ic_overview"
app:showAsAction="ifRoom" app:showAsAction="ifRoom|withText"
android:id="@+id/whats_new" /> android:id="@+id/whats_new" />
<item <item
android:title="@string/main_menu__categories" android:title="@string/main_menu__categories"
android:icon="@drawable/ic_category" android:icon="@drawable/ic_category"
app:showAsAction="ifRoom" app:showAsAction="ifRoom|withText"
android:id="@+id/categories" /> android:id="@+id/categories" />
<item <item
android:title="@string/main_menu__swap_nearby" android:title="@string/main_menu__swap_nearby"
android:icon="@drawable/ic_nearby" android:icon="@drawable/ic_nearby"
app:showAsAction="ifRoom" app:showAsAction="ifRoom|withText"
android:id="@+id/nearby" /> android:id="@+id/nearby" />
<item <item
android:title="@string/main_menu__my_apps" android:title="@string/preference_category__my_apps"
android:icon="@drawable/ic_my_apps" android:icon="@drawable/ic_my_apps"
app:showAsAction="ifRoom" app:showAsAction="ifRoom|withText"
android:id="@+id/my_apps" /> android:id="@+id/my_apps" />
<item <item
android:title="@string/menu_settings" android:title="@string/menu_settings"
android:icon="@drawable/ic_settings" android:icon="@drawable/ic_settings"
app:showAsAction="ifRoom" app:showAsAction="ifRoom|withText"
android:id="@+id/settings" /> android:id="@+id/settings" />
</menu> </menu>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- The material design specs indicate a 24dp margin on tablets should be used (hence this
file differing from values-v21/dimens.xml):
https://material.google.com/components/buttons-floating-action-button.html -->
<dimen name="fab_margin">24dp</dimen>
</resources>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="DetailsButtonStyle" parent="DetailsButtonStyleBase">
<item name="android:paddingStart">20dp</item>
<item name="android:paddingEnd">20dp</item>
</style>
</resources>

View File

@ -18,6 +18,8 @@
<!-- The selected item stands out from the background by this elevation --> <!-- The selected item stands out from the background by this elevation -->
<dimen name="details_screenshot_selected_elevation">3dp</dimen> <dimen name="details_screenshot_selected_elevation">3dp</dimen>
<dimen name="fab_margin">16dp</dimen>
<dimen name="whats_new__padding__app_card__horizontal">12dp</dimen> <dimen name="whats_new__padding__app_card__horizontal">12dp</dimen>
<dimen name="whats_new__padding__app_card__vertical">10dp</dimen> <dimen name="whats_new__padding__app_card__vertical">10dp</dimen>

View File

@ -65,6 +65,12 @@
<string name="app_inst_unknown_source">Installed (from unknown source)</string> <string name="app_inst_unknown_source">Installed (from unknown source)</string>
<string name="app_version_x_available">Version %1$s available</string> <string name="app_version_x_available">Version %1$s available</string>
<string name="app_version_x_installed">Version %1$s</string> <string name="app_version_x_installed">Version %1$s</string>
<string name="app_recommended_version_installed">Version %1$s (Recommended)</string>
<string name="app__newly_added">New</string>
<string name="installed_apps__activity_title">Installed Apps</string>
<string name="installed_app__updates_ignored">Updates ignored</string>
<string name="installed_app__updates_ignored_for_suggested_version">Updates ignored for Version %1$s</string>
<string name="added_on">Added on %s</string> <string name="added_on">Added on %s</string>
@ -142,7 +148,9 @@
<string name="main_menu__latest_apps">Latest</string> <string name="main_menu__latest_apps">Latest</string>
<string name="main_menu__categories">Categories</string> <string name="main_menu__categories">Categories</string>
<string name="main_menu__swap_nearby">Nearby</string> <string name="main_menu__swap_nearby">Nearby</string>
<string name="main_menu__my_apps">My Apps</string>
<string name="preference_category__my_apps">My Apps</string>
<string name="preference_manage_installed_apps">Manage Installed Apps</string>
<string name="details_installed">Version %s installed</string> <string name="details_installed">Version %s installed</string>
@ -444,5 +452,11 @@
<string name="notification_action_cancel">Cancel</string> <string name="notification_action_cancel">Cancel</string>
<string name="notification_action_install">Install</string> <string name="notification_action_install">Install</string>
<string name="category">Category</string> <!-- Used by the TTS engine when showing a category "Chip" in the search box -->
<string name="tts_category_name">Category %1$s</string>
<plurals name="tts_view_all_in_category">
<item quantity="one">View the single one app in the %2$s category</item>
<item quantity="other">View all %1$d apps from the %2$s category</item>
</plurals>
</resources> </resources>

View File

@ -1,17 +1,22 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<style name="DetailsPrimaryButtonStyle"> <style name="DetailsButtonStyleBase">
<item name="android:padding">5dp</item> <item name="android:paddingTop">5dp</item>
<item name="android:paddingBottom">5dp</item>
<item name="android:paddingLeft">20dp</item>
<item name="android:paddingRight">20dp</item>
<item name="android:textSize">12sp</item> <item name="android:textSize">12sp</item>
<item name="android:textStyle">normal</item> <item name="android:textStyle">normal</item>
</style>
<style name="DetailsButtonStyle" parent="DetailsButtonStyleBase" />
<style name="DetailsPrimaryButtonStyle" parent="DetailsButtonStyle">
<item name="android:textColor">#ffffff</item> <item name="android:textColor">#ffffff</item>
<item name="android:background">@drawable/button_primary_background_selector</item> <item name="android:background">@drawable/button_primary_background_selector</item>
</style> </style>
<style name="DetailsSecondaryButtonStyle"> <style name="DetailsSecondaryButtonStyle" parent="DetailsButtonStyle">
<item name="android:padding">5dp</item>
<item name="android:textSize">12sp</item>
<item name="android:textStyle">normal</item>
<item name="android:textColor">@color/fdroid_blue</item> <item name="android:textColor">@color/fdroid_blue</item>
<item name="android:background">@drawable/button_secondary_background_selector</item> <item name="android:background">@drawable/button_secondary_background_selector</item>
</style> </style>

View File

@ -1,6 +1,12 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="@string/updates"> <PreferenceCategory android:title="@string/preference_category__my_apps">
<PreferenceScreen android:title="@string/preference_manage_installed_apps">
<intent
android:action="android.intent.action.MAIN"
android:targetPackage="org.fdroid.fdroid"
android:targetClass="org.fdroid.fdroid.views.installed.InstalledAppsActivity" />
</PreferenceScreen>
<PreferenceScreen <PreferenceScreen
android:title="@string/menu_manage" android:title="@string/menu_manage"
android:summary="@string/repositories_summary"> android:summary="@string/repositories_summary">
@ -9,6 +15,8 @@
android:targetPackage="org.fdroid.fdroid" android:targetPackage="org.fdroid.fdroid"
android:targetClass="org.fdroid.fdroid.views.ManageReposActivity" /> android:targetClass="org.fdroid.fdroid.views.ManageReposActivity" />
</PreferenceScreen> </PreferenceScreen>
</PreferenceCategory>
<PreferenceCategory android:title="@string/updates">
<com.geecko.QuickLyric.view.AppCompatListPreference android:title="@string/update_interval" <com.geecko.QuickLyric.view.AppCompatListPreference android:title="@string/update_interval"
android:key="updateInterval" android:key="updateInterval"
android:defaultValue="24" android:defaultValue="24"

View File

@ -1,9 +1,12 @@
package org.fdroid.fdroid; package org.fdroid.fdroid;
import android.content.ContentProvider;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.ContextWrapper; import android.content.ContextWrapper;
import android.content.pm.ProviderInfo;
import org.mockito.AdditionalAnswers; import org.mockito.AdditionalAnswers;
import org.robolectric.Robolectric;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import org.robolectric.shadows.ShadowContentResolver; import org.robolectric.shadows.ShadowContentResolver;
@ -22,6 +25,12 @@ public class TestUtils {
@SuppressWarnings("unused") @SuppressWarnings("unused")
private static final String TAG = "TestUtils"; // NOPMD private static final String TAG = "TestUtils"; // NOPMD
public static <T extends ContentProvider> void registerContentProvider(String authority, Class<T> providerClass) {
ProviderInfo info = new ProviderInfo();
info.authority = authority;
Robolectric.buildContentProvider(providerClass).create(info);
}
public static File copyResourceToTempFile(String resourceName) { public static File copyResourceToTempFile(String resourceName) {
File tempFile = null; File tempFile = null;
InputStream input = null; InputStream input = null;

View File

@ -6,7 +6,7 @@ import android.content.Context;
import org.fdroid.fdroid.views.AppDetailsRecyclerViewAdapter; import org.fdroid.fdroid.views.AppDetailsRecyclerViewAdapter;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RobolectricGradleTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
@ -16,9 +16,8 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
// TODO: Use sdk=24 when Robolectric supports this @Config(constants = BuildConfig.class, sdk = 24)
@Config(constants = BuildConfig.class, sdk = 23) @RunWith(RobolectricTestRunner.class)
@RunWith(RobolectricGradleTestRunner.class)
public class UtilsTest { public class UtilsTest {
String fdroidFingerprint = "43238D512C1E5EB2D6569F4A3AFBF5523418B82E0A3ED1552770ABB9A9C9CCAB"; String fdroidFingerprint = "43238D512C1E5EB2D6569F4A3AFBF5523418B82E0A3ED1552770ABB9A9C9CCAB";

View File

@ -13,7 +13,7 @@ import org.fdroid.fdroid.mock.MockApk;
import org.fdroid.fdroid.mock.MockRepo; import org.fdroid.fdroid.mock.MockRepo;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RobolectricGradleTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import java.util.ArrayList; import java.util.ArrayList;
@ -29,9 +29,8 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
// TODO: Use sdk=24 when Robolectric supports this @Config(constants = BuildConfig.class, application = Application.class, sdk = 24)
@Config(constants = BuildConfig.class, application = Application.class, sdk = 23) @RunWith(RobolectricTestRunner.class)
@RunWith(RobolectricGradleTestRunner.class)
public class ApkProviderTest extends FDroidProviderTest { public class ApkProviderTest extends FDroidProviderTest {
private static final String[] PROJ = Cols.ALL; private static final String[] PROJ = Cols.ALL;

View File

@ -4,26 +4,25 @@ import android.app.Application;
import org.fdroid.fdroid.Assert; import org.fdroid.fdroid.Assert;
import org.fdroid.fdroid.BuildConfig; import org.fdroid.fdroid.BuildConfig;
import org.fdroid.fdroid.TestUtils;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RobolectricGradleTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowContentResolver;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
// TODO: Use sdk=24 when Robolectric supports this @Config(constants = BuildConfig.class, application = Application.class, sdk = 24)
@Config(constants = BuildConfig.class, application = Application.class, sdk = 23) @RunWith(RobolectricTestRunner.class)
@RunWith(RobolectricGradleTestRunner.class)
public class AppPrefsProviderTest extends FDroidProviderTest { public class AppPrefsProviderTest extends FDroidProviderTest {
@Before @Before
public void setup() { public void setup() {
ShadowContentResolver.registerProvider(AppProvider.getAuthority(), new AppProvider()); TestUtils.registerContentProvider(AppProvider.getAuthority(), AppProvider.class);
} }
@SuppressWarnings({"PMD.EqualsNull", "EqualsWithItself", "EqualsBetweenInconvertibleTypes", "ObjectEqualsNull"}) @SuppressWarnings({"PMD.EqualsNull", "EqualsWithItself", "EqualsBetweenInconvertibleTypes", "ObjectEqualsNull"})

View File

@ -8,11 +8,12 @@ import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
import org.fdroid.fdroid.BuildConfig; import org.fdroid.fdroid.BuildConfig;
import org.fdroid.fdroid.TestUtils;
import org.fdroid.fdroid.data.Schema.AppMetadataTable.Cols; import org.fdroid.fdroid.data.Schema.AppMetadataTable.Cols;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RobolectricGradleTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowContentResolver; import org.robolectric.shadows.ShadowContentResolver;
@ -27,16 +28,15 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
// TODO: Use sdk=24 when Robolectric supports this @Config(constants = BuildConfig.class, application = Application.class, sdk = 24)
@Config(constants = BuildConfig.class, application = Application.class, sdk = 23) @RunWith(RobolectricTestRunner.class)
@RunWith(RobolectricGradleTestRunner.class)
public class AppProviderTest extends FDroidProviderTest { public class AppProviderTest extends FDroidProviderTest {
private static final String[] PROJ = Cols.ALL; private static final String[] PROJ = Cols.ALL;
@Before @Before
public void setup() { public void setup() {
ShadowContentResolver.registerProvider(AppProvider.getAuthority(), new AppProvider()); TestUtils.registerContentProvider(AppProvider.getAuthority(), AppProvider.class);
} }
/** /**
@ -115,7 +115,7 @@ public class AppProviderTest extends FDroidProviderTest {
assertFalse(notInstalled.canAndWantToUpdate(context)); assertFalse(notInstalled.canAndWantToUpdate(context));
assertResultCount(contentResolver, 2, AppProvider.getCanUpdateUri(), PROJ); assertResultCount(contentResolver, 2, AppProvider.getCanUpdateUri(), PROJ);
assertResultCount(contentResolver, 7, AppProvider.getInstalledUri(), PROJ); assertResultCount(contentResolver, 9, AppProvider.getInstalledUri(), PROJ);
App installedOnlyOneVersionAvailable = AppProvider.Helper.findSpecificApp(r, "installed, only one version available", 1, Cols.ALL); App installedOnlyOneVersionAvailable = AppProvider.Helper.findSpecificApp(r, "installed, only one version available", 1, Cols.ALL);
App installedAlreadyLatestNoIgnore = AppProvider.Helper.findSpecificApp(r, "installed, already latest, no ignore", 1, Cols.ALL); App installedAlreadyLatestNoIgnore = AppProvider.Helper.findSpecificApp(r, "installed, already latest, no ignore", 1, Cols.ALL);

View File

@ -7,28 +7,27 @@ import android.net.Uri;
import org.fdroid.fdroid.BuildConfig; import org.fdroid.fdroid.BuildConfig;
import org.fdroid.fdroid.R; import org.fdroid.fdroid.R;
import org.fdroid.fdroid.TestUtils;
import org.fdroid.fdroid.data.Schema.AppMetadataTable.Cols; import org.fdroid.fdroid.data.Schema.AppMetadataTable.Cols;
import org.fdroid.fdroid.mock.MockRepo; import org.fdroid.fdroid.mock.MockRepo;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RobolectricGradleTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowContentResolver;
import java.util.List; import java.util.List;
import static org.fdroid.fdroid.Assert.assertContainsOnly; import static org.fdroid.fdroid.Assert.assertContainsOnly;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
// TODO: Use sdk=24 when Robolectric supports this
@Config(constants = BuildConfig.class, application = Application.class, sdk = 23) @Config(constants = BuildConfig.class, application = Application.class, sdk = 23)
@RunWith(RobolectricGradleTestRunner.class) @RunWith(RobolectricTestRunner.class)
public class CategoryProviderTest extends FDroidProviderTest { public class CategoryProviderTest extends FDroidProviderTest {
@Before @Before
public void setup() { public void setup() {
ShadowContentResolver.registerProvider(AppProvider.getAuthority(), new AppProvider()); TestUtils.registerContentProvider(AppProvider.getAuthority(), AppProvider.class);
} }
// ======================================================================== // ========================================================================

View File

@ -13,15 +13,14 @@ import org.fdroid.fdroid.Utils;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RobolectricGradleTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import org.robolectric.Shadows; import org.robolectric.Shadows;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowContentResolver; import org.robolectric.shadows.ShadowContentResolver;
// TODO: Use sdk=24 when Robolectric supports this @Config(constants = BuildConfig.class, application = Application.class, sdk = 24)
@Config(constants = BuildConfig.class, application = Application.class, sdk = 23) @RunWith(RobolectricTestRunner.class)
@RunWith(RobolectricGradleTestRunner.class)
public class DatabaseMigration { public class DatabaseMigration {
protected ShadowContentResolver contentResolver; protected ShadowContentResolver contentResolver;
@ -31,7 +30,7 @@ public class DatabaseMigration {
public final void setupBase() { public final void setupBase() {
contentResolver = Shadows.shadowOf(RuntimeEnvironment.application.getContentResolver()); contentResolver = Shadows.shadowOf(RuntimeEnvironment.application.getContentResolver());
context = TestUtils.createContextWithContentResolver(contentResolver); context = TestUtils.createContextWithContentResolver(contentResolver);
ShadowContentResolver.registerProvider(AppProvider.getAuthority(), new AppProvider()); TestUtils.registerContentProvider(AppProvider.getAuthority(), AppProvider.class);
} }
@Test @Test

View File

@ -18,7 +18,7 @@ public abstract class FDroidProviderTest {
public final void setupBase() { public final void setupBase() {
contentResolver = Shadows.shadowOf(RuntimeEnvironment.application.getContentResolver()); contentResolver = Shadows.shadowOf(RuntimeEnvironment.application.getContentResolver());
context = TestUtils.createContextWithContentResolver(contentResolver); context = TestUtils.createContextWithContentResolver(contentResolver);
ShadowContentResolver.registerProvider(AppProvider.getAuthority(), new AppProvider()); TestUtils.registerContentProvider(AppProvider.getAuthority(), AppProvider.class);
} }
@After @After

View File

@ -6,14 +6,14 @@ import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
import org.fdroid.fdroid.BuildConfig; import org.fdroid.fdroid.BuildConfig;
import org.fdroid.fdroid.TestUtils;
import org.fdroid.fdroid.data.Schema.InstalledAppTable.Cols; import org.fdroid.fdroid.data.Schema.InstalledAppTable.Cols;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RobolectricGradleTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowContentResolver;
import static org.fdroid.fdroid.Assert.assertIsInstalledVersionInDb; import static org.fdroid.fdroid.Assert.assertIsInstalledVersionInDb;
import static org.fdroid.fdroid.Assert.assertResultCount; import static org.fdroid.fdroid.Assert.assertResultCount;
@ -23,14 +23,13 @@ import static org.junit.Assert.assertTrue;
import java.util.Map; import java.util.Map;
// TODO: Use sdk=24 when Robolectric supports this @Config(constants = BuildConfig.class, application = Application.class, sdk = 24)
@Config(constants = BuildConfig.class, application = Application.class, sdk = 23) @RunWith(RobolectricTestRunner.class)
@RunWith(RobolectricGradleTestRunner.class)
public class InstalledAppProviderTest extends FDroidProviderTest { public class InstalledAppProviderTest extends FDroidProviderTest {
@Before @Before
public void setup() { public void setup() {
ShadowContentResolver.registerProvider(InstalledAppProvider.getAuthority(), new InstalledAppProvider()); TestUtils.registerContentProvider(InstalledAppProvider.getAuthority(), InstalledAppProvider.class);
} }
@Test @Test

View File

@ -8,7 +8,7 @@ import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RobolectricGradleTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment; import org.robolectric.RuntimeEnvironment;
import org.robolectric.Shadows; import org.robolectric.Shadows;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
@ -20,9 +20,8 @@ import java.util.List;
import static org.fdroid.fdroid.Assert.assertInvalidUri; import static org.fdroid.fdroid.Assert.assertInvalidUri;
import static org.fdroid.fdroid.Assert.assertValidUri; import static org.fdroid.fdroid.Assert.assertValidUri;
// TODO: Use sdk=24 when Robolectric supports this @Config(constants = BuildConfig.class, sdk = 24)
@Config(constants = BuildConfig.class, sdk = 23) @RunWith(RobolectricTestRunner.class)
@RunWith(RobolectricGradleTestRunner.class)
public class ProviderUriTests { public class ProviderUriTests {
private ShadowContentResolver resolver; private ShadowContentResolver resolver;
@ -39,14 +38,14 @@ public class ProviderUriTests {
@Test @Test
public void invalidInstalledAppProviderUris() { public void invalidInstalledAppProviderUris() {
ShadowContentResolver.registerProvider(InstalledAppProvider.getAuthority(), new InstalledAppProvider()); TestUtils.registerContentProvider(InstalledAppProvider.getAuthority(), InstalledAppProvider.class);
assertInvalidUri(resolver, InstalledAppProvider.getAuthority()); assertInvalidUri(resolver, InstalledAppProvider.getAuthority());
assertInvalidUri(resolver, "blah"); assertInvalidUri(resolver, "blah");
} }
@Test @Test
public void validInstalledAppProviderUris() { public void validInstalledAppProviderUris() {
ShadowContentResolver.registerProvider(InstalledAppProvider.getAuthority(), new InstalledAppProvider()); TestUtils.registerContentProvider(InstalledAppProvider.getAuthority(), InstalledAppProvider.class);
String[] projection = new String[] {InstalledAppTable.Cols._ID}; String[] projection = new String[] {InstalledAppTable.Cols._ID};
assertValidUri(resolver, InstalledAppProvider.getContentUri(), projection); assertValidUri(resolver, InstalledAppProvider.getContentUri(), projection);
assertValidUri(resolver, InstalledAppProvider.getAppUri("org.example.app"), projection); assertValidUri(resolver, InstalledAppProvider.getAppUri("org.example.app"), projection);
@ -58,14 +57,14 @@ public class ProviderUriTests {
@Test @Test
public void invalidRepoProviderUris() { public void invalidRepoProviderUris() {
ShadowContentResolver.registerProvider(RepoProvider.getAuthority(), new RepoProvider()); TestUtils.registerContentProvider(RepoProvider.getAuthority(), RepoProvider.class);
assertInvalidUri(resolver, RepoProvider.getAuthority()); assertInvalidUri(resolver, RepoProvider.getAuthority());
assertInvalidUri(resolver, "blah"); assertInvalidUri(resolver, "blah");
} }
@Test @Test
public void validRepoProviderUris() { public void validRepoProviderUris() {
ShadowContentResolver.registerProvider(RepoProvider.getAuthority(), new RepoProvider()); TestUtils.registerContentProvider(RepoProvider.getAuthority(), RepoProvider.class);
String[] projection = new String[] {Schema.RepoTable.Cols._ID}; String[] projection = new String[] {Schema.RepoTable.Cols._ID};
assertValidUri(resolver, RepoProvider.getContentUri(), projection); assertValidUri(resolver, RepoProvider.getContentUri(), projection);
assertValidUri(resolver, RepoProvider.getContentUri(10000L), projection); assertValidUri(resolver, RepoProvider.getContentUri(10000L), projection);
@ -74,14 +73,14 @@ public class ProviderUriTests {
@Test @Test
public void invalidAppProviderUris() { public void invalidAppProviderUris() {
ShadowContentResolver.registerProvider(AppProvider.getAuthority(), new AppProvider()); TestUtils.registerContentProvider(AppProvider.getAuthority(), AppProvider.class);
assertInvalidUri(resolver, AppProvider.getAuthority()); assertInvalidUri(resolver, AppProvider.getAuthority());
assertInvalidUri(resolver, "blah"); assertInvalidUri(resolver, "blah");
} }
@Test @Test
public void validAppProviderUris() { public void validAppProviderUris() {
ShadowContentResolver.registerProvider(AppProvider.getAuthority(), new AppProvider()); TestUtils.registerContentProvider(AppProvider.getAuthority(), AppProvider.class);
String[] projection = new String[] {Schema.AppMetadataTable.Cols._ID}; String[] projection = new String[] {Schema.AppMetadataTable.Cols._ID};
assertValidUri(resolver, AppProvider.getContentUri(), "content://org.fdroid.fdroid.data.AppProvider", projection); assertValidUri(resolver, AppProvider.getContentUri(), "content://org.fdroid.fdroid.data.AppProvider", projection);
assertValidUri(resolver, AppProvider.getSearchUri("'searching!'", null), "content://org.fdroid.fdroid.data.AppProvider/search/'searching!'", projection); assertValidUri(resolver, AppProvider.getSearchUri("'searching!'", null), "content://org.fdroid.fdroid.data.AppProvider/search/'searching!'", projection);
@ -105,7 +104,7 @@ public class ProviderUriTests {
@Test @Test
public void validTempAppProviderUris() { public void validTempAppProviderUris() {
ShadowContentResolver.registerProvider(TempAppProvider.getAuthority(), new TempAppProvider()); TestUtils.registerContentProvider(TempAppProvider.getAuthority(), TempAppProvider.class);
String[] projection = new String[]{Schema.AppMetadataTable.Cols._ID}; String[] projection = new String[]{Schema.AppMetadataTable.Cols._ID};
// Required so that the `assertValidUri` calls below will indeed have a real temp_fdroid_app // Required so that the `assertValidUri` calls below will indeed have a real temp_fdroid_app
@ -122,14 +121,14 @@ public class ProviderUriTests {
@Test @Test
public void invalidApkProviderUris() { public void invalidApkProviderUris() {
ShadowContentResolver.registerProvider(ApkProvider.getAuthority(), new ApkProvider()); TestUtils.registerContentProvider(ApkProvider.getAuthority(), ApkProvider.class);
assertInvalidUri(resolver, ApkProvider.getAuthority()); assertInvalidUri(resolver, ApkProvider.getAuthority());
assertInvalidUri(resolver, "blah"); assertInvalidUri(resolver, "blah");
} }
@Test @Test
public void validApkProviderUris() { public void validApkProviderUris() {
ShadowContentResolver.registerProvider(ApkProvider.getAuthority(), new ApkProvider()); TestUtils.registerContentProvider(ApkProvider.getAuthority(), ApkProvider.class);
String[] projection = new String[] {Schema.ApkTable.Cols._ID}; String[] projection = new String[] {Schema.ApkTable.Cols._ID};
List<Apk> apks = new ArrayList<>(10); List<Apk> apks = new ArrayList<>(10);

View File

@ -32,7 +32,7 @@ import org.fdroid.fdroid.Utils;
import org.fdroid.fdroid.data.Schema.RepoTable; import org.fdroid.fdroid.data.Schema.RepoTable;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RobolectricGradleTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import java.util.List; import java.util.List;
@ -41,9 +41,8 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
// TODO: Use sdk=24 when Robolectric supports this @Config(constants = BuildConfig.class, application = Application.class, sdk = 24)
@Config(constants = BuildConfig.class, application = Application.class, sdk = 23) @RunWith(RobolectricTestRunner.class)
@RunWith(RobolectricGradleTestRunner.class)
public class RepoProviderTest extends FDroidProviderTest { public class RepoProviderTest extends FDroidProviderTest {
private static final String[] COLS = RepoTable.Cols.ALL; private static final String[] COLS = RepoTable.Cols.ALL;

View File

@ -12,7 +12,7 @@ import org.fdroid.fdroid.data.RepoProvider;
import org.fdroid.fdroid.data.Schema.RepoTable.Cols; import org.fdroid.fdroid.data.Schema.RepoTable.Cols;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RobolectricGradleTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import java.util.List; import java.util.List;
@ -20,9 +20,8 @@ import java.util.List;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
// TODO: Use sdk=24 when Robolectric supports this @Config(constants = BuildConfig.class, sdk = 24)
@Config(constants = BuildConfig.class, sdk = 23) @RunWith(RobolectricTestRunner.class)
@RunWith(RobolectricGradleTestRunner.class)
public class AcceptableMultiRepoUpdaterTest extends MultiRepoUpdaterTest { public class AcceptableMultiRepoUpdaterTest extends MultiRepoUpdaterTest {
private static final String TAG = "AcceptableMultiRepoTest"; private static final String TAG = "AcceptableMultiRepoTest";

View File

@ -6,7 +6,7 @@ import org.fdroid.fdroid.RepoUpdater;
import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.Utils;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RobolectricGradleTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
/** /**
@ -14,9 +14,8 @@ import org.robolectric.annotation.Config;
* because there is so much metadata to parse in the main repo, covering many different aspects * because there is so much metadata to parse in the main repo, covering many different aspects
* of the available metadata. Some apps will be added, others updated, and it should all just work. * of the available metadata. Some apps will be added, others updated, and it should all just work.
*/ */
// TODO: Use sdk=24 when Robolectric supports this @Config(constants = BuildConfig.class, sdk = 24)
@Config(constants = BuildConfig.class, sdk = 23) @RunWith(RobolectricTestRunner.class)
@RunWith(RobolectricGradleTestRunner.class)
public class FDroidRepoUpdateTest extends MultiRepoUpdaterTest { public class FDroidRepoUpdateTest extends MultiRepoUpdaterTest {
private static final String TAG = "FDroidRepoUpdateTest"; private static final String TAG = "FDroidRepoUpdateTest";

View File

@ -12,16 +12,15 @@ import org.fdroid.fdroid.data.Schema;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RobolectricGradleTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import java.util.List; import java.util.List;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
// TODO: Use sdk=24 when Robolectric supports this @Config(constants = BuildConfig.class, sdk = 24)
@Config(constants = BuildConfig.class, sdk = 23) @RunWith(RobolectricTestRunner.class)
@RunWith(RobolectricGradleTestRunner.class)
public class Issue763MultiRepo extends MultiRepoUpdaterTest { public class Issue763MultiRepo extends MultiRepoUpdaterTest {
private Repo microGRepo; private Repo microGRepo;

View File

@ -18,7 +18,7 @@ import org.fdroid.fdroid.data.Schema.AppMetadataTable;
import org.fdroid.fdroid.data.Schema.RepoTable.Cols; import org.fdroid.fdroid.data.Schema.RepoTable.Cols;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RobolectricGradleTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
@ -30,9 +30,8 @@ import java.util.Map;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
// TODO: Use sdk=24 when Robolectric supports this @Config(constants = BuildConfig.class, sdk = 24)
@Config(constants = BuildConfig.class, sdk = 23) @RunWith(RobolectricTestRunner.class)
@RunWith(RobolectricGradleTestRunner.class)
public class ProperMultiRepoUpdaterTest extends MultiRepoUpdaterTest { public class ProperMultiRepoUpdaterTest extends MultiRepoUpdaterTest {
private static final String TAG = "ProperMultiRepoSupport"; private static final String TAG = "ProperMultiRepoSupport";

View File

@ -36,7 +36,7 @@ import org.fdroid.fdroid.mock.RepoDetails;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RobolectricGradleTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLog; import org.robolectric.shadows.ShadowLog;
@ -56,9 +56,8 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
// TODO: Use sdk=24 when Robolectric supports this @Config(constants = BuildConfig.class, sdk = 24)
@Config(constants = BuildConfig.class, sdk = 23) @RunWith(RobolectricTestRunner.class)
@RunWith(RobolectricGradleTestRunner.class)
public class RepoXMLHandlerTest { public class RepoXMLHandlerTest {
private static final String TAG = "RepoXMLHandlerTest"; private static final String TAG = "RepoXMLHandlerTest";

View File

@ -18,14 +18,13 @@ import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RobolectricGradleTestRunner; import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config; import org.robolectric.annotation.Config;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
// TODO: Use sdk=24 when Robolectric supports this @Config(constants = BuildConfig.class, application = Application.class, sdk = 24)
@Config(constants = BuildConfig.class, application = Application.class, sdk = 23) @RunWith(RobolectricTestRunner.class)
@RunWith(RobolectricGradleTestRunner.class)
public class AppDetailsAdapterTest extends FDroidProviderTest { public class AppDetailsAdapterTest extends FDroidProviderTest {
@Before @Before

Some files were not shown because too many files have changed in this diff Show More