Explorar o código

Open from homescreen/add shortcut to launcher (#435)

* Add very basic "Add to homescreen" action in manga info fragment.

* Fix open from homescreen opening current manga (if a manga is open).
Code cleanup.

* Improve fix for "Opening from homescreen opens currently open manga if a manga is currently open" and fix "Going back to the main app via a Manga opened through a shortcut repeats the launcher open animation".

* Implement custom icons, add star icon and optimize some things.

* Remove Tachiyomi and custom image icon types.

* Move icon creation task into an observable.
Added some extra error handling.
Andy Bao %!s(int64=8) %!d(string=hai) anos
pai
achega
d352405ba6

+ 2 - 0
app/build.gradle

@@ -153,6 +153,8 @@ dependencies {
     // Image library
     compile 'com.github.bumptech.glide:glide:3.7.0'
     compile 'com.github.bumptech.glide:okhttp3-integration:1.4.0@aar'
+    // Transformations
+    compile 'jp.wasabeef:glide-transformations:2.0.1'
 
     // Logging
     compile 'com.jakewharton.timber:timber:4.3.0'

+ 3 - 1
app/src/main/AndroidManifest.xml

@@ -8,6 +8,7 @@
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
+    <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
 
     <application
         android:name=".App"
@@ -28,7 +29,8 @@
         </activity>
         <activity
             android:name=".ui.manga.MangaActivity"
-            android:parentActivityName=".ui.main.MainActivity" >
+            android:parentActivityName=".ui.main.MainActivity"
+            android:exported="true">
         </activity>
         <activity
             android:name=".ui.reader.ReaderActivity"

+ 6 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaActivity.kt

@@ -24,6 +24,7 @@ class MangaActivity : BaseRxActivity<MangaPresenter>() {
 
         const val FROM_CATALOGUE_EXTRA = "from_catalogue"
         const val MANGA_EXTRA = "manga"
+        const val FROM_LAUNCHER_EXTRA = "from_launcher"
         const val INFO_FRAGMENT = 0
         const val CHAPTERS_FRAGMENT = 1
         const val MYANIMELIST_FRAGMENT = 2
@@ -47,6 +48,11 @@ class MangaActivity : BaseRxActivity<MangaPresenter>() {
         super.onCreate(savedState)
         setContentView(R.layout.activity_manga)
 
+        val fromLauncher = intent.getBooleanExtra(FROM_LAUNCHER_EXTRA, false)
+
+        //Remove any current manga if we are launching from launcher
+        if(fromLauncher) SharedData.remove(MangaEvent::class.java)
+
         presenter.setMangaEvent(SharedData.getOrPut(MangaEvent::class.java) {
             val id = intent.getLongExtra(MANGA_EXTRA, 0)
             MangaEvent(presenter.db.getManga(id).executeAsBlocking()!!)

+ 99 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoFragment.kt

@@ -1,21 +1,44 @@
 package eu.kanade.tachiyomi.ui.manga.info
 
+import android.app.Activity
 import android.content.Intent
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.graphics.drawable.BitmapDrawable
 import android.net.Uri
 import android.os.Bundle
 import android.support.customtabs.CustomTabsIntent
+import android.support.design.widget.Snackbar
+import android.util.SparseArray
 import android.view.*
+import com.afollestad.materialdialogs.MaterialDialog
+import com.bumptech.glide.BitmapRequestBuilder
+import com.bumptech.glide.BitmapTypeRequest
 import com.bumptech.glide.Glide
 import com.bumptech.glide.load.engine.DiskCacheStrategy
+import com.bumptech.glide.load.resource.bitmap.CenterCrop
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.data.database.models.Manga
 import eu.kanade.tachiyomi.data.source.Source
 import eu.kanade.tachiyomi.data.source.online.OnlineSource
 import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment
+import eu.kanade.tachiyomi.ui.library.LibraryFragment
+import eu.kanade.tachiyomi.ui.manga.MangaActivity
 import eu.kanade.tachiyomi.util.getResourceColor
 import eu.kanade.tachiyomi.util.toast
+import jp.wasabeef.glide.transformations.CropCircleTransformation
+import jp.wasabeef.glide.transformations.CropSquareTransformation
+import jp.wasabeef.glide.transformations.MaskTransformation
+import jp.wasabeef.glide.transformations.RoundedCornersTransformation
 import kotlinx.android.synthetic.main.fragment_manga_info.*
+import kotlinx.android.synthetic.main.item_download.*
 import nucleus.factory.RequiresPresenter
+import rx.Observable
+import rx.android.schedulers.AndroidSchedulers
+import rx.schedulers.Schedulers
+import timber.log.Timber
+import java.io.IOException
+import kotlin.concurrent.thread
 
 /**
  * Fragment that shows manga information.
@@ -34,6 +57,7 @@ class MangaInfoFragment : BaseRxFragment<MangaInfoPresenter>() {
         fun newInstance(): MangaInfoFragment {
             return MangaInfoFragment()
         }
+
     }
 
     override fun onCreate(savedState: Bundle?) {
@@ -61,6 +85,7 @@ class MangaInfoFragment : BaseRxFragment<MangaInfoPresenter>() {
         when (item.itemId) {
             R.id.action_open_in_browser -> openInBrowser()
             R.id.action_share -> shareManga()
+            R.id.action_add_to_home_screen -> addToHomeScreen()
             else -> return super.onOptionsItemSelected(item)
         }
         return true
@@ -178,6 +203,80 @@ class MangaInfoFragment : BaseRxFragment<MangaInfoPresenter>() {
         }
     }
 
+    /**
+     * Add the manga to the home screen
+     */
+    fun addToHomeScreen() {
+        val shortcutIntent = activity.intent
+        shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
+                .putExtra(MangaActivity.FROM_LAUNCHER_EXTRA, true)
+
+        val addIntent = Intent()
+        addIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent)
+                .action = "com.android.launcher.action.INSTALL_SHORTCUT"
+
+        //Set shortcut title
+        MaterialDialog.Builder(activity)
+                .title(R.string.shortcut_title)
+                .input("", presenter.manga.title, { md, text ->
+                    //Set shortcut title
+                    addIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, text.toString())
+
+                    reshapeIconBitmap(addIntent,
+                            Glide.with(context).load(presenter.manga).asBitmap())
+                })
+                .negativeText(android.R.string.cancel)
+                .onNegative { materialDialog, dialogAction -> materialDialog.cancel() }
+                .show()
+    }
+
+    fun reshapeIconBitmap(addIntent: Intent, request: BitmapTypeRequest<out Any>) {
+        val modes = intArrayOf(R.string.circular_icon,
+                R.string.rounded_icon,
+                R.string.square_icon,
+                R.string.star_icon)
+
+        fun BitmapRequestBuilder<out Any, Bitmap>.toIcon(): Bitmap {
+            return this.into(96, 96).get()
+        }
+
+        MaterialDialog.Builder(activity)
+                .title(R.string.icon_shape)
+                .negativeText(android.R.string.cancel)
+                .items(modes.map { getString(it) })
+                .itemsCallback { dialog, view, i, charSequence ->
+                    Observable.fromCallable {
+                        // i = 0: Circular icon
+                        // i = 1: Rounded icon
+                        // i = 2: Square icon
+                        // i = 3: Star icon (because boredom)
+                        when (i) {
+                            0 -> request.transform(CropCircleTransformation(context)).toIcon()
+                            1 -> request.transform(RoundedCornersTransformation(context, 5, 0)).toIcon()
+                            2 -> request.transform(CropSquareTransformation(context)).toIcon()
+                            3 -> request.transform(CenterCrop(context), MaskTransformation(context, R.drawable.mask_star)).toIcon()
+                            else -> null
+                        }
+                    }.subscribeOn(Schedulers.io())
+                    .observeOn(AndroidSchedulers.mainThread())
+                    .subscribe({ if (it != null) createShortcut(addIntent, it) },
+                            { context.toast(R.string.icon_creation_fail) })
+                }.show()
+    }
+
+    fun createShortcut(addIntent: Intent, icon: Bitmap) {
+        //Send shortcut intent
+        addIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON, icon)
+        context.sendBroadcast(addIntent)
+        //Go to launcher to show this shiny new shortcut!
+        val startMain = Intent(Intent.ACTION_MAIN)
+        startMain.addCategory(Intent.CATEGORY_HOME)
+                .flags = Intent.FLAG_ACTIVITY_NEW_TASK
+        activity.runOnUiThread {
+            startActivity(startMain)
+        }
+    }
+
     /**
      * Update FAB with correct drawable.
      *

+ 9 - 0
app/src/main/res/drawable/ic_home_white_24dp.xml

@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#ffffff"
+        android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z"/>
+</vector>

BIN=BIN
app/src/main/res/drawable/mask_star.png


+ 4 - 0
app/src/main/res/menu/manga_info.xml

@@ -12,4 +12,8 @@
         android:title="@string/action_open_in_browser"
         app:showAsAction="never"/>
 
+    <item android:id="@+id/action_add_to_home_screen"
+        android:title="@string/action_add_to_home_screen"
+        app:showAsAction="never"/>
+
 </menu>

+ 12 - 0
app/src/main/res/values/strings.xml

@@ -51,6 +51,7 @@
     <string name="action_remove">Remove</string>
     <string name="action_resume">Resume</string>
     <string name="action_open_in_browser">Open in browser</string>
+    <string name="action_add_to_home_screen">Add to home screen</string>
     <string name="action_display_mode">Change display mode</string>
     <string name="action_set_filter">Set filter</string>
     <string name="action_cancel">Cancel</string>
@@ -225,6 +226,16 @@
     <string name="manga_info_genres_label">Genres</string>
     <string name="share_subject">Share…</string>
     <string name="share_text">Check out %1$s! at %2$s</string>
+    <string name="added_to_home_screen">Manga added to home screen</string>
+    <string name="icon_type">Icon type</string>
+    <string name="tachiyomi_icon">Tachiyomi icon</string>
+    <string name="circular_icon">Circular icon</string>
+    <string name="rounded_icon">Rounded icon</string>
+    <string name="square_icon">Square icon</string>
+    <string name="star_icon">Star icon</string>
+    <string name="shortcut_title">Shortcut title</string>
+    <string name="icon_shape">Icon shape</string>
+    <string name="icon_creation_fail">Failed to create shortcut!</string>
 
     <!-- Manga chapters fragment -->
     <string name="manga_chapters_tab">Chapters</string>
@@ -305,6 +316,7 @@
     <!-- File Picker Titles -->
     <string name="file_select_cover">Select cover image</string>
     <string name="file_select_backup">Select backup file</string>
+    <string name="file_select_icon">Select shortcut icon</string>
 
     <!--UpdateCheck-->
     <string name="update_check_title">New update available!</string>