Skip to content

Commit 66a4aaa

Browse files
committed
[Android] Various finishing touches
1 parent 6735df0 commit 66a4aaa

File tree

39 files changed

+383
-119
lines changed

39 files changed

+383
-119
lines changed

MALCLient.UrlInterceptor/MALCLient.UrlInterceptor.nuget.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
88
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\dogie\.nuget\packages\</NuGetPackageFolders>
99
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">ProjectJson</NuGetProjectStyle>
10-
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">4.0.0</NuGetToolVersion>
10+
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">4.1.0</NuGetToolVersion>
1111
</PropertyGroup>
1212
<PropertyGroup>
1313
<MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>

MALClient.Adapters/IChangeLogProvider.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ namespace MALClient.Adapters
88
{
99
public interface IChangeLogProvider
1010
{
11+
bool NewChangelog { get; set; }
12+
string CurrentVersion { get; }
13+
1114
string DateWithVersion { get; }
1215
List<string> Changelog { get; }
1316
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
6+
using Android.App;
7+
using Android.Content;
8+
using Android.OS;
9+
using Android.Runtime;
10+
using Android.Views;
11+
using Android.Widget;
12+
using GalaSoft.MvvmLight.Ioc;
13+
using MALClient.Adapters;
14+
15+
namespace MALClient.Android.Adapters
16+
{
17+
public class ChangelogProvider : IChangeLogProvider
18+
{
19+
public ChangelogProvider()
20+
{
21+
var context = SimpleIoc.Default.GetInstance<Activity>();
22+
var package = context.PackageManager.GetPackageInfo(context.PackageName, 0);
23+
_currentVersion = package.VersionName;
24+
25+
}
26+
27+
private string _currentVersion;
28+
29+
30+
public string CurrentVersion => _currentVersion;
31+
32+
public bool NewChangelog { get; set; }
33+
34+
public string DateWithVersion => $"{_currentVersion} - 06.04.2017";
35+
36+
public List<string> Changelog => new List<string>
37+
{
38+
"Ahoy!",
39+
};
40+
41+
}
42+
}

MALClient.Android.Adapters/MALClient.Android.Adapters.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
</ItemGroup>
6565
<ItemGroup>
6666
<Compile Include="ApplicationDataServiceService.cs" />
67+
<Compile Include="ChangelogProvider.cs" />
6768
<Compile Include="ClipboardProvider.cs" />
6869
<Compile Include="DataCache.cs" />
6970
<Compile Include="ImageDownloaderService.cs" />

MALClient.Android.Adapters/PasswordVaultProvider.cs

Lines changed: 113 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
using MALClient.Adapters.Credentials;
1+
using System;
2+
using System.IO;
3+
using System.Linq;
4+
using System.Security.Cryptography;
5+
using System.Text;
6+
using MALClient.Adapters.Credentials;
27
using MALClient.Models.AdapterModels;
38
using MALClient.XShared.ViewModels;
49

@@ -8,10 +13,8 @@ public class PasswordVaultProvider : IPasswordVault
813
{
914
public void Add(VaultCredential credential)
1015
{
11-
//var vault = new PasswordVault();
12-
//vault.Add(new PasswordCredential(credential.Domain, credential.UserName, credential.Password));
1316
ResourceLocator.ApplicationDataService["Username"] = credential.UserName;
14-
ResourceLocator.ApplicationDataService["Passwd"] = credential.Password;
17+
ResourceLocator.ApplicationDataService["Passwd"] = StringCipher.Encrypt(credential.Password);
1518
}
1619

1720
public VaultCredential Get(string domain)
@@ -20,7 +23,9 @@ public VaultCredential Get(string domain)
2023
//var credential = vault.FindAllByResource("MALClient").FirstOrDefault();
2124
//credential.RetrievePassword();
2225
//return new VaultCredential(credential.Resource, credential.UserName, credential.Password);
23-
var credential = new VaultCredential("MALClient", ResourceLocator.ApplicationDataService["Username"] as string, ResourceLocator.ApplicationDataService["Passwd"] as string);
26+
var credential = new VaultCredential("MALClient",
27+
ResourceLocator.ApplicationDataService["Username"] as string,
28+
StringCipher.Decrypt(ResourceLocator.ApplicationDataService["Passwd"] as string));
2429
return credential.Password == null || credential.UserName == null ? null : credential;
2530
}
2631

@@ -33,4 +38,107 @@ public void Reset()
3338
// vault.Remove(passwordCredential);
3439
}
3540
}
41+
42+
/// <summary>
43+
/// http://stackoverflow.com/questions/10168240/encrypting-decrypting-a-string-in-c-sharp
44+
/// </summary>
45+
static class StringCipher
46+
{
47+
// This constant is used to determine the keysize of the encryption algorithm in bits.
48+
// We divide this by 8 within the code below to get the equivalent number of bytes.
49+
private const int Keysize = 256;
50+
51+
// This constant determines the number of iterations for the password bytes generation function.
52+
private const int DerivationIterations = 1000;
53+
54+
public static string Encrypt(string plainText, string passPhrase = "cEEfr93GGdpyV76UARrVJBzNVcWqjpLXFgsEvwPELaebCPkH") //TODO the thing
55+
{
56+
if (string.IsNullOrEmpty(plainText))
57+
return plainText;
58+
// Salt and IV is randomly generated each time, but is preprended to encrypted cipher text
59+
// so that the same Salt and IV values can be used when decrypting.
60+
var saltStringBytes = Generate256BitsOfRandomEntropy();
61+
var ivStringBytes = Generate256BitsOfRandomEntropy();
62+
var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
63+
using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
64+
{
65+
var keyBytes = password.GetBytes(Keysize / 8);
66+
using (var symmetricKey = new RijndaelManaged())
67+
{
68+
symmetricKey.BlockSize = 256;
69+
symmetricKey.Mode = CipherMode.CBC;
70+
symmetricKey.Padding = PaddingMode.PKCS7;
71+
using (var encryptor = symmetricKey.CreateEncryptor(keyBytes, ivStringBytes))
72+
{
73+
using (var memoryStream = new MemoryStream())
74+
{
75+
using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
76+
{
77+
cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
78+
cryptoStream.FlushFinalBlock();
79+
// Create the final bytes as a concatenation of the random salt bytes, the random iv bytes and the cipher bytes.
80+
var cipherTextBytes = saltStringBytes;
81+
cipherTextBytes = cipherTextBytes.Concat(ivStringBytes).ToArray();
82+
cipherTextBytes = cipherTextBytes.Concat(memoryStream.ToArray()).ToArray();
83+
memoryStream.Close();
84+
cryptoStream.Close();
85+
return Convert.ToBase64String(cipherTextBytes);
86+
}
87+
}
88+
}
89+
}
90+
}
91+
}
92+
93+
public static string Decrypt(string cipherText, string passPhrase = "cEEfr93GGdpyV76UARrVJBzNVcWqjpLXFgsEvwPELaebCPkH")
94+
{
95+
if (string.IsNullOrEmpty(cipherText))
96+
return cipherText;
97+
// Get the complete stream of bytes that represent:
98+
// [32 bytes of Salt] + [32 bytes of IV] + [n bytes of CipherText]
99+
var cipherTextBytesWithSaltAndIv = Convert.FromBase64String(cipherText);
100+
// Get the saltbytes by extracting the first 32 bytes from the supplied cipherText bytes.
101+
var saltStringBytes = cipherTextBytesWithSaltAndIv.Take(Keysize / 8).ToArray();
102+
// Get the IV bytes by extracting the next 32 bytes from the supplied cipherText bytes.
103+
var ivStringBytes = cipherTextBytesWithSaltAndIv.Skip(Keysize / 8).Take(Keysize / 8).ToArray();
104+
// Get the actual cipher text bytes by removing the first 64 bytes from the cipherText string.
105+
var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip((Keysize / 8) * 2).Take(cipherTextBytesWithSaltAndIv.Length - ((Keysize / 8) * 2)).ToArray();
106+
107+
using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations))
108+
{
109+
var keyBytes = password.GetBytes(Keysize / 8);
110+
using (var symmetricKey = new RijndaelManaged())
111+
{
112+
symmetricKey.BlockSize = 256;
113+
symmetricKey.Mode = CipherMode.CBC;
114+
symmetricKey.Padding = PaddingMode.PKCS7;
115+
using (var decryptor = symmetricKey.CreateDecryptor(keyBytes, ivStringBytes))
116+
{
117+
using (var memoryStream = new MemoryStream(cipherTextBytes))
118+
{
119+
using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
120+
{
121+
var plainTextBytes = new byte[cipherTextBytes.Length];
122+
var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
123+
memoryStream.Close();
124+
cryptoStream.Close();
125+
return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
126+
}
127+
}
128+
}
129+
}
130+
}
131+
}
132+
133+
private static byte[] Generate256BitsOfRandomEntropy()
134+
{
135+
var randomBytes = new byte[32]; // 32 Bytes will give us 256 bits.
136+
using (var rngCsp = new RNGCryptoServiceProvider())
137+
{
138+
// Fill the array with cryptographically secure random bytes.
139+
rngCsp.GetBytes(randomBytes);
140+
}
141+
return randomBytes;
142+
}
143+
}
36144
}

MALClient.Android/Activities/MainActivity.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Linq;
33
using System.Text.RegularExpressions;
4+
using System.Threading.Tasks;
45
using Android.App;
56
using Android.Content;
67
using Android.Content.PM;
@@ -11,11 +12,13 @@
1112
using GalaSoft.MvvmLight.Ioc;
1213
using HockeyApp.Android;
1314
using HockeyApp.Android.Metrics;
15+
using MALClient.Android.DIalogs;
1416
using MALClient.Android.Fragments;
1517
using MALClient.Android.Resources;
1618
using MALClient.Android.ViewModels;
1719
using MALClient.Models.Enums;
1820
using MALClient.Models.Models.Notifications;
21+
using MALClient.XShared.BL;
1922
using MALClient.XShared.Comm.Anime;
2023
using MALClient.XShared.Comm.MagicalRawQueries;
2124
using MALClient.XShared.Comm.Manga;
@@ -48,7 +51,7 @@ public MainActivity()
4851
SimpleIoc.Default.Register<Activity>(() => this);
4952
}
5053

51-
protected override void OnCreate(Bundle bundle)
54+
protected override async void OnCreate(Bundle bundle)
5255
{
5356
RequestedOrientation = ScreenOrientation.Unspecified;
5457

@@ -78,6 +81,12 @@ protected override void OnCreate(Bundle bundle)
7881

7982
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().PermitAll().Build();
8083
StrictMode.SetThreadPolicy(policy);
84+
85+
InitializationRoutines.InitPostUpdate();
86+
87+
await Task.Delay(1000);
88+
if (ResourceLocator.ChangelogProvider.NewChangelog)
89+
ChangelogDialog.BuildChangelogDialog(ResourceLocator.ChangelogProvider);
8190
}
8291

8392
#if !DEBUG

MALClient.Android/Activities/MainActivity.ui.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ private void InitBindings()
6464
Bindings[MainPageRefreshButton.Id].Add(
6565
this.SetBinding(() => ViewModel.RefreshButtonVisibility,
6666
() => MainPageRefreshButton.Visibility).ConvertSourceToTarget(Converters.BoolToVisibility));
67+
MainPageRefreshButton.Click += (sender, args) => ViewModel.RefreshDataCommand.Execute(null);
6768

6869
Bindings.Add(MainPageSearchView.Id, new List<Binding>());
6970
Bindings[MainPageSearchView.Id].Add(

MALClient.Android/Adapters/CollectionAdapters/AlternatingListCollectionAdapter.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,7 @@ public override View GetView(int position, View convertView, ViewGroup parent)
3131
if (view == null)
3232
{
3333
view = _context.LayoutInflater.Inflate(_layoutResource,null);
34-
view.SetBackgroundColor(
35-
new Color(position%2 == _alternator
36-
? ResourceExtension.BrushRowAlternate1
37-
: ResourceExtension.BrushRowAlternate2));
34+
3835
InitializeView(view,_items[position]);
3936
}
4037
return view;

MALClient.Android/AndroidViewModelLocator.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ public static void RegisterDependencies()
5050
SimpleIoc.Default.Register<INotificationsTaskManager, NotificationTaskManager>();
5151
SimpleIoc.Default.Register<ISchdeuledJobsManger, ScheduledJobsManager>();
5252
SimpleIoc.Default.Register<ICssManager, CssManager>();
53+
SimpleIoc.Default.Register<IChangeLogProvider, ChangelogProvider>();
5354

5455
}
5556

MALClient.Android/BackgroundTasks/NotificationCheckBroadcastReceiver.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,8 @@ private async void ScheduleToast(Context context, MalNotification notification)
168168
.SetSmallIcon(Resource.Drawable.badge_icon)
169169
.SetStyle(new NotificationCompat.BigTextStyle().BigText(notification.Content))
170170
.SetContentTitle(notification.Header)
171+
.SetContentText(notification.Content)
172+
.SetContentInfo(notification.Content)
171173
.SetAutoCancel(true)
172174
.SetContentIntent(pendingIntent)
173175
.SetSound(RingtoneManager.GetDefaultUri(RingtoneType.Notification));
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
6+
using Android.App;
7+
using Android.Content;
8+
using Android.Graphics;
9+
using Android.OS;
10+
using Android.Runtime;
11+
using Android.Views;
12+
using Android.Widget;
13+
using Com.Orhanobut.Dialogplus;
14+
using FFImageLoading;
15+
using FFImageLoading.Views;
16+
using GalaSoft.MvvmLight.Command;
17+
using GalaSoft.MvvmLight.Helpers;
18+
using MALClient.Adapters;
19+
using MALClient.Android.Activities;
20+
using MALClient.Android.BindingConverters;
21+
using MALClient.Android.Listeners;
22+
using MALClient.Android.Listeners.DialogListeners;
23+
using MALClient.Android.Resources;
24+
using MALClient.XShared.ViewModels;
25+
26+
namespace MALClient.Android.DIalogs
27+
{
28+
public static class ChangelogDialog
29+
{
30+
private static DialogPlus _changelogDialog;
31+
32+
public static void BuildChangelogDialog(IChangeLogProvider changeLogProvider)
33+
{
34+
var dialogBuilder = DialogPlus.NewDialog(MainActivity.CurrentContext);
35+
dialogBuilder.SetGravity((int)(GravityFlags.Top));
36+
dialogBuilder.SetMargin(DimensionsHelper.DpToPx(40), DimensionsHelper.DpToPx(75), DimensionsHelper.DpToPx(2), 0);
37+
dialogBuilder.SetContentHolder(new ViewHolder(Resource.Layout.ChangelogDialog));
38+
dialogBuilder.SetContentBackgroundResource(Resource.Color.Transparent);
39+
dialogBuilder.SetOnDismissListener(
40+
new DialogDismissedListener(() => ViewModelLocator.NavMgr.ResetOneTimeOverride()));
41+
ViewModelLocator.NavMgr.RegisterOneTimeMainOverride(new RelayCommand(CleanupChangelogDialog));
42+
_changelogDialog = dialogBuilder.Create();
43+
var dialogView = _changelogDialog.HolderView;
44+
45+
dialogView.FindViewById<TextView>(Resource.Id.ChangelogDialogHeader).Text = changeLogProvider.DateWithVersion;
46+
dialogView.FindViewById(Resource.Id.ChangelogDialogCloseButton).SetOnClickListener(new OnClickListener(view => _changelogDialog.Dismiss()));
47+
dialogView.FindViewById<LinearLayout>(Resource.Id.ChangelogDialogChangesList).SetAdapter(changeLogProvider.Changelog.GetAdapter(
48+
(i, s, arg3) =>
49+
{
50+
var view = new TextView(MainActivity.CurrentContext);
51+
view.SetTextColor(new Color(ResourceExtension.BrushText));
52+
view.Text = $"• {s}";
53+
return view;
54+
}));
55+
56+
_changelogDialog.Show();
57+
}
58+
59+
private static void CleanupChangelogDialog()
60+
{
61+
_changelogDialog.Dismiss();
62+
}
63+
}
64+
}

MALClient.Android/Fragments/AnimeDetailsPageFragment.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ protected override void InitBindings()
5454
AnimeDetailsPageTabStrip.SetViewPager(AnimeDetailsPagePivot);
5555
AnimeDetailsPageTabStrip.CenterTabs();
5656
AnimeDetailsPagePivot.SetCurrentItem(_navArgs.SourceTabIndex,false);
57+
AnimeDetailsPagePivot.OffscreenPageLimit = 7;
5758

5859
AnimeDetailsPagePivot.AddOnPageChangeListener(new OnPageChangedListener(i => ViewModel.DetailsPivotSelectedIndex = i));
5960

0 commit comments

Comments
 (0)